Странные результаты с функцией потолка C ++ - PullRequest
5 голосов
/ 06 апреля 2011

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

#include <stdio.h>
#include <cmath>

int main ()
{
cout << "The ceiling of " << 411 << " is " << ceil(411) << endl;
cout << "The ceiling of 4.11*100  is " << ceil(4.11*100) << endl;

cout << "The ceiling of  " << 121 << " is " << ceil(121) << endl;
cout << "The ceiling of 1.21*100  is " << ceil(1.21*100) << endl;;
}


OUTPUT:

The ceiling of 411 is 411
The ceiling of 4.11*100  is 412
The ceiling of  121 is 121
The ceiling of 1.21*100  is 121

Ответы [ 3 ]

9 голосов
/ 06 апреля 2011

Проблема в том, что числа с плавающей запятой не могут быть надежно представлены компьютером. Это означает, что 4.11 не представлен как 4.11, но что-то очень близко к нему. И когда это "очень близко к 4.11" число умножается на 100, ceil продукта оказывается 412, к вашему удивлению! Но как только вы узнаете, как числа с плавающей запятой хранятся и извлекаются, это совсем не удивительно.


Просто посмотрите эту интересную демонстрацию:

float a = 3.2; //3.2 is double!
if ( a == 3.2 )
    cout << "a is equal to 3.2"<<endl;
else
    cout << "a is not equal to 3.2"<<endl;

float b = 3.2f; //3.2f is a float. Note: f is appended to make it float!
if ( b == 3.2f )
    cout << "b is equal to 3.2f"<<endl;
else
    cout << "b is not equal to 3.2f"<<endl;

Выход:

а не равно 3,2
b равно 3,2f

Экспериментируйте здесь на ideone: http://www.ideone.com/pAGzM

Попробуйте изменить тип переменной a с float на double, см. результат еще раз .

5 голосов
/ 06 апреля 2011

Из FAQ :

[29.16] Почему с плавающей запятой так неточны? Почему это не печать 0,43

#include <iostream>

 int main()
 {
   float a = 1000.43;
   float b = 1000.0;
   std::cout << a - b << '\n';
   ...
 }

Отказ от ответственности : разочарование по поводу округления / усечения / приближение на самом деле не проблема C ++; это проблема информатики. Тем не мение, люди продолжают спрашивать об этом на comp.lang.c ++, так что следует номинальный ответ.

Ответ : Плавающая точка приближение. Стандарт IEEE для 32-битный float поддерживает 1 бит знака, 8 биты экспоненты и 23 бита мантисса. С нормализованным мантисса бинарной точки всегда имеет форма 1.xxxxx ... ведущая 1 упал, и вы получите эффективно 24 кусочки мантиссы. Номер 1000.43 (и многие, многие другие, в том числе некоторые действительно распространенные, такие как 0,1) не точно представлен в поплавке или двойной формат. 1000.43 на самом деле представлен в следующем bitpattern («s» показывает позицию знака бита, "е" показывают позиции показательных битов и «м» показывают положение биты мантиссы):

 seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm
 01000100011110100001101110000101

Сдвинутая мантисса 1111101000.01101110000101 или 1000 + 7045/16384. Дробная часть +0,429992675781. С 24 битами мантиссы вы получите только 1 часть в 16M точности для поплавка. Двойной тип обеспечивает большую точность (53 бита мантиссы).

Также см. [29.17] Почему не работает сравнение с плавающей запятой?

1 голос
/ 06 апреля 2011

Функция ceil(x) возвращает наименьшее целое число не менее x.

Поскольку введенные вами константы (например, 4.11 или 1.21) не представлены точно, они могут быть представлены немного меньшим числом или немного большим числом или в редких случаях равными числами. Например. ваш компилятор представляет константу 4.11 как немного большее число, поэтому 4.11*100 оказывается немного больше, чем 411, поэтому ceil(4.11*100) == 412 (потому что 412 - это наименьшее число, не меньшее, чем число, немного большее, чем 411), но 1.21 - это немного меньше, поэтому 1.21*100 немного меньше 121, поэтому ceil(1.21*100)==121.

Также обратите внимание, что умножение тоже не точное.

...