Добавление числа с плавающей запятой увеличено вдвое?

StackOverflow https://stackoverflow.com/questions/1839225

  •  12-09-2019
  •  | 
  •  

Вопрос

Сегодня утром у меня случился небольшой WTF-момент.Этот WTF можно резюмировать следующим образом:

float x = 0.2f;
float y = 0.1f;
float z = x + y;
assert(z == x + y); //This assert is triggered! (Atleast with visual studio 2008)

Причина, видимо, в том, что выражение x + y увеличивается вдвое и сравнивается с усеченной версией в z.(Если я изменю z к double утверждение не срабатывает).

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

4.6.1."Значение типа float может быть преобразовано в значение rvalue типа double.Стоимость не изменилась"

Мой вопрос в том, является ли x + y гарантированно будет повышен до удвоения или это на усмотрение составителя?

ОБНОВЛЯТЬ: Поскольку многие люди утверждают, что не следует использовать == Что касается плавающей запятой, я просто хотел заявить, что в конкретном случае, с которым я работаю, точное сравнение оправдано.

Сравнение с плавающей запятой является сложно, вот интересно связь по теме, которая, я думаю, не была упомянута.

Это было полезно?

Решение

Вы не можете вообще предполагать, что == будет работать должным образом для типов с плавающей запятой.Сравните округленные значения или используйте такие конструкции, как abs(a-b) < tolerance вместо.

Повышение полностью на усмотрение компилятора (и будет зависеть от целевого оборудования, уровня оптимизации и т. д.).

В этом конкретном случае почти наверняка значения хранятся в регистрах FPU с более высокой точностью, чем в памяти - в общем, современное оборудование FPU работает с двойной или более высокой точностью внутри, независимо от точности, которую запросил программист, с помощью компилятора, генерирующего код. выполнять соответствующие преобразования при сохранении значений в памяти;в неоптимизированной сборке результат x+y все еще находится в регистре в момент сравнения, но z будут сохранены в памяти и извлечены обратно и, таким образом, усечены до точности с плавающей запятой.

Другие советы

А Рабочий проект следующего стандарта C++0x в разделе 5 пункте 11 сказано

Значения плавающих операндов и результаты плавающих выражений могут быть представлены с большей точностью и диапазоном, чем это требуется для типа;типы при этом не изменяются

Так что на усмотрение компилятора.

Используя gcc 4.3.2, утверждение нет сработало, и действительно, значение rvalue вернулось из x + y это float, а не double.

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

В C++ FAQ lite есть дальнейшее обсуждение этой темы:

Это проблема, поскольку преобразование числа с плавающей запятой в двоичный код не дает точной точности.

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

См. ниже, например.

float x = 0.25f; //both fits within 4 bytes with precision
float y = 0.50f;
float z = x + y;
assert(z == x + y); // it would work fine and no assert

Я думаю, что это будет на усмотрение компилятора, но вы всегда можете принудительно выполнить приведение типов, если вы так думаете?

Еще одна причина никогда не сравнивать числа с плавающей запятой напрямую.

if (fabs(result - expectedResult) < 0.00001)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top