То, каким образом перекрывают () окружает ровное плавание без дробных частей?
Когда я пытаюсь сделать это:
double x = 2.22;
x *= 100; //which becomes 222.00...
printf("%lf", ceil(x)); //prints 223.00... (?)
Но когда я изменяю значение 2,22 к 2,21
x *= 100; //which becomes 221.00...
printf("%lf", ceil(x)); //prints 221.00... as expected
Я попытался сделать это иначе как это использование modf () и встретился с другой странной вещью:
double x = 2.22 * 100;
double num, fraction;
fraction = modf(x, &num);
if(fraction > 0)
num += 1; //goes inside here even when fraction is 0.00...
Таким образом, то, что происходит, 0.000... больше, чем 0?
Кто-либо может объяснить, почему обе из этих ситуаций происходят? Также я компилирую использование cc версия 4.1.2 в Redhat.
Это нормально, поскольку числа хранятся в двоичном формате. В то время как ваши числа могут быть записаны конечным числом цифр с использованием десятичной дроби, это не относится к двоичной системе.
Вам следует прочитать отчет Голдберга под названием Что должен знать каждый компьютерный ученый об арифметике с плавающей запятой .
Основной ответ заключается в том, что число с плавающей запятой, которое вы получаете с:
double x = 2.22;
, на самом деле немного больше чем значение 2.22
, а значение, которое вы получаете с
double x = 2.21;
, немного меньше, чем 2.21
.
Не все значения с плавающей запятой могут быть правильно представлены типами float
и double
C - скорее всего, вы думаете, что 222
на самом деле что-то вроде 222.00000000000000001
.
Существует стандартный, но малоизвестный способ обойти это - использовать функцию nextafter ()
C99:
printf("%lf", ceil(nextafter(x, 0)));
Подробнее см. man nextafter
.
Это потому, что 2,22 - это не совсем 2,22 и, следовательно, 2,22 * 100 не 222, а 222,000000000x
double x = 2.22;
printf("%.20lf\n", x); //prints 2.22000000000000019540
x *= 100;
printf("%.20lf\n", x); //prints 222.00000000000002842171
Если вам нужна целочисленная точность (например, для расчета денежных вещи) использовать интегральную арифметику (т.е. вычислять центы вместо долларов).
Если бы вы написали:
const int x = 1.2;
в программе на языке C, что бы произошло? Литерал 1.2
не может быть представлен как целочисленное значение, поэтому компилятор преобразует в соответствии с обычными правилами в целое число, и x
будет присвоено значение 1
.
Здесь происходит то же самое.
Вы писали:
double x = 2.22;
2.22
не может быть представлено как число двойной точности, поэтому компилятор преобразует его в соответствии с правилами стандарта C. Вы получите ближайшее представимое число с двойной точностью, а именно:
2.220000000000000195399252334027551114559173583984375
Когда это значение умножается на 100 в double
, результат будет:
222.000000000000028421709430404007434844970703125
и когда вы вызовете ceil ()
с этим значением в качестве аргумента математическая библиотека правильно возвращает 223.0
.
Вы можете рассмотреть возможность использования десятичных типов с плавающей точкой, чтобы получить результаты, ожидаемые от десятичной арифметики. Аппаратное обеспечение типичных процессоров настольных ПК поддерживает только двоичную систему с плавающей запятой, поэтому нельзя ожидать, что она даст те же результаты, что и десятичные вычисления. Конечно, если аппаратная поддержка отсутствует, десятичная плавающая точка работает медленнее.
Обратите внимание, что десятичная плавающая точка не обязательно является более точной (например, 1/3 не может быть представлена точно, так же как и в двоичном FP есть значения, которые не могут быть представлены), она просто даст ожидаемый результат, как если бы вы выполняли вычисления вручную в десятичной системе счисления.