проблема с двойным усечением и математикой в ​​C - PullRequest
3 голосов
/ 17 января 2012

Я создаю функции, которые помещают шары в коробки.код, который вычисляет количество шариков, которые могут поместиться на каждой стороне коробки, приведен ниже.Предположим, что шары сочетаются друг с другом, как если бы они были кубиками.Я знаю, что это не оптимальный путь, но просто продолжайте.

проблема для меня в том, что, хотя я получаю числа, такие как 4.0000000 * 4.0000000 * 2.000000, продукт равен 31 вместо 32. что происходит ??

две дополнительные вещи, эта ошибка возникает только при достижении оптимальной длины стороны;например, длина стороны равна 12,2, толщина коробки равна 0,1, а радиус шара равен 1,5.это приводит к тому, что на эту сторону вписывается ровно 4 шара.если я не выполняю приведение как int, это работает, но если я выполняю приведение как int, я получаю вышеупомянутую ошибку (31 вместо 32).Кроме того, линия печати запускается один раз, если длина стороны оптимальна, но дважды, если это не так.Я не знаю, что это значит.

double ballsFit(double r, double l, double w, double h, double boxthick)
{
double ballsInL, ballsInW, ballsInH;
int ballsinbox;

ballsInL= (int)((l-(2*boxthick))/(r*2));
ballsInW= (int)((w-(2*boxthick))/(r*2));
ballsInH= (int)((h-(2*boxthick))/(r*2));
ballsinbox=(ballsInL*ballsInW*ballsInH);
printf("LENGTH=%f\nWidth=%f\nHight=%f\nBALLS=%d\n", ballsInL, ballsInW, ballsInH, ballsinbox);
return ballsinbox;
}

Ответы [ 2 ]

6 голосов
/ 17 января 2012

Основная проблема в том, что математика с плавающей точкой является неточной.

Например, число 0.1, которое вы упоминаете в качестве значения толщины в проблемном примере, не может быть представлено в точности какdouble.Когда вы присваиваете 0,1 переменной, то сохраняется приближение 0,1.

Я рекомендую вам прочитать Что должен знать каждый компьютерный специалист об арифметике с плавающей точкой .

хотя я получаю цифры вроде 4.0000000 * 4.0000000 * 2.000000, продукт равен 31 вместо 32. что происходит ??

Это почти наверняка такмультипликаторы (по крайней мере, некоторые из них) - это не то, на что они похожи.Если бы они были точно 4.0, 4.0 и 2.0, их продукт был бы точно 32.0.Если вы распечатали все цифры, которые могут представлять двойные числа, я почти уверен, что вы увидите много девяти, как в 3.99999999999 ... и т. Д. Как следствие, продукт чуть меньше 32.преобразование double-to-int просто отсекает дробную часть, так что вы получите 31.

Конечно, вы не всегда получаете числа, которые на меньше , чем они были быесли расчет был точным;Вы также можете получить числа, которые на больше , чем вы могли бы ожидать.

1 голос
/ 17 января 2012

Числа с плавающей запятой фиксированной точности, такие как IEEE-754 , обычно используемые в современных компьютерах, не могут точно представлять все десятичные числа - так же, как 1/3 не может быть точно представлено в десятичном виде.

Например, 0.1 может быть чем-то вроде 0.100000000000000004... при преобразовании в двоичный файл и обратно. Разница небольшая, но значительная.

Мне иногда удавалось (частично) решать такие проблемы, используя расширенную или произвольную арифметику точности, чтобы поддерживать степень точности при вычислениях, а затем преобразовывать с понижением до double для получения окончательных результатов. Обычно наблюдается заметное снижение производительности, но ИМХО правильность бесконечно важнее.

Недавно я использовал алгоритмы из высокоточных арифметических библиотек, перечисленных здесь , с хорошими результатами как по точности, так и по производительности.

...