проблемы в сравнении с плавающей запятой
void main() { float f = 0.98; if(f <= 0.98) printf("hi"); else printf("hello"); getch(); }
Я получаю эту проблему здесь. Используя разные значения с плавающей запятой, получаю разные результаты. Почему это происходит?
- Ассемблер x86: сравнение с плавающей запятой
- Как выполнить поразрядную операцию с числами с плавающей запятой
- Клавиши с плавающей запятой в std: map
- проблема с плавающей запятой в R?
- Как может примитивное значение float быть -0.0? Что это значит?
- Преобразование String для плавания в Apple Swift
- Как вы объедините число с плавающей запятой в Perl?
- Точность с плавающей запятой C ++
f
использует точность float
, но 0.98 по умолчанию имеет double
точность, поэтому оператор f <= 0.98
сравнивается с использованием double
точности.
Таким образом, f
сравнивается с double
, но может сделать результат немного большим, чем 0,98.
использование
if(f <= 0.98f)
или вместо этого используйте double
for f
.
Подробно ... предполагая, что float
- это IEEE с одной точностью, а double
- двойная точность IEEE .
Эти типы чисел с плавающей запятой сохраняются с представлением base-2. В base-2 это число нуждается в бесконечной точности для представления, поскольку это повторяющееся десятичное число:
0.98 = 0.1111101011100001010001111010111000010100011110101110000101000...
float
может хранить только 24 бита значимых цифр, т. Е.
0.111110101110000101000111_101... ^ round off here = 0.111110101110000101001000 = 16441672 / 2^24 = 0.98000001907...
double
может хранить 53 бит знаковых цифр, поэтому
0.11111010111000010100011110101110000101000111101011100_00101000... ^ round off here = 0.11111010111000010100011110101110000101000111101011100 = 8827055269646172 / 2^53 = 0.97999999999999998224...
Таким образом, 0,98 станет немного больше float
и меньше в double
.
Это связано с тем, что значения с плавающей запятой не являются точными представлениями числа. Все базовые десять номеров должны быть представлены на компьютере в качестве базовых чисел. Именно в этом преобразовании точность теряется.
Подробнее об этом читайте на http://en.wikipedia.org/wiki/Floating_point
Пример (от встречи с этой проблемой в мои дни VB6)
Чтобы преобразовать число 1.1 в одноточечное число с плавающей запятой, нам нужно преобразовать его в двоичный. Необходимо создать 32 бита.
Бит 1 – это знаковый бит (отрицательный [1] или позиция [0]). Биты 2-9 приведены для значения экспоненты. Биты 10-32 для мантиссы (также известный как коэффициент научной нотации)
Таким образом, для 1.1 одиночное значение с плавающей запятой сохраняется следующим образом (это усеченное значение, компилятор может округлить наименее значимый бит за кулисами, но все, что я делаю, обрезает его, что немного менее точно, но не изменяет результаты этого примера):
s --exp--- -------mantissa-------- 0 01111111 00011001100110011001100
Если вы заметили, что в мантиссе есть повторяющийся шаблон 0011. 1/10 в двоичном формате равно 1/3 в десятичном значении. Это продолжается вечно. Таким образом, чтобы получить значения из 32-битного значения с плавающей запятой с одиночной точностью, мы должны сначала преобразовать показатель экспоненты и мантиссы в десятичные числа, чтобы мы могли их использовать.
sign = 0 = положительное число
Показатель: 01111111 = 127
Мантисса: 00011001100110011001100 = 838860
С мантиссой нам нужно преобразовать ее в десятичное значение. Причина в том, что перед двоичным номером подразумевается целое число (например, 1.00011001100110011001100). Подразумеваемое число состоит в том, что мантисса представляет нормированное значение, которое должно использоваться в научной нотации: 1.0001100110011 …. * 2 ^ (x-127).
Чтобы получить десятичное значение из 838860, мы просто делим на 2 ^ -23, так как в мантиссе 23 бита. Это дает нам 0.099999904632568359375. Добавьте подразумеваемый 1 к мантиссе, дает нам 1.099999904632568359375. Показатель равен 127, но формула требует 2 ^ (x-127).
Итак, вот математика:
(1 + 099999904632568359375) * 2 ^ (127-127)
1.099999904632568359375 * 1 = 1.099999904632568359375
Как вы можете видеть, 1.1 не хранится в одном значении с плавающей запятой как 1.1.