math.h перекрывают не работу как ожидалось в C

То, каким образом перекрывают () окружает ровное плавание без дробных частей?
Когда я пытаюсь сделать это:

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.

6
задан bojaboja 22 February 2010 в 13:55
поделиться

6 ответов

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

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

12
ответ дан 8 December 2019 в 02:40
поделиться

Основной ответ заключается в том, что число с плавающей запятой, которое вы получаете с:

double x = 2.22;

, на самом деле немного больше чем значение 2.22 , а значение, которое вы получаете с

double x = 2.21;

, немного меньше, чем 2.21 .

12
ответ дан 8 December 2019 в 02:40
поделиться

Не все значения с плавающей запятой могут быть правильно представлены типами float и double C - скорее всего, вы думаете, что 222 на самом деле что-то вроде 222.00000000000000001 .

Существует стандартный, но малоизвестный способ обойти это - использовать функцию nextafter () C99:

printf("%lf", ceil(nextafter(x, 0)));

Подробнее см. man nextafter .

7
ответ дан 8 December 2019 в 02:40
поделиться

Это потому, что 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

Если вам нужна целочисленная точность (например, для расчета денежных вещи) использовать интегральную арифметику (т.е. вычислять центы вместо долларов).

2
ответ дан 8 December 2019 в 02:40
поделиться

Если бы вы написали:

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
ответ дан 8 December 2019 в 02:40
поделиться

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

Обратите внимание, что десятичная плавающая точка не обязательно является более точной (например, 1/3 не может быть представлена точно, так же как и в двоичном FP есть значения, которые не могут быть представлены), она просто даст ожидаемый результат, как если бы вы выполняли вычисления вручную в десятичной системе счисления.

0
ответ дан 8 December 2019 в 02:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: