Является ли литерал double для преобразования с плавающей точкой равным литералу с плавающей точкой? - PullRequest
3 голосов
/ 29 марта 2019

Может ли быть разница в битовом представлении между прямым присваиванием литерала с плавающей точкой float x = 3.2f; и double, неявно преобразованного в число с плавающей запятой float x2 = 3.2;?

т.е. есть

#define EQUAL(FLOAT_LITERAL)\
  FLOAT_LITERAL##f == static_cast<float>(FLOAT_LITERAL)
EQUAL(3.2) && EQUAL(55.6200093490) // etc ... 

true для всех литералов с плавающей запятой?

Я задаю этот вопрос, потому что clang или gcc не жалуются на сужение конверсий, если числа находятся в диапазоне значений с плавающей запятой: Предупреждение включается с -Wnarrowing:

float f {3.422222222222222222222222222222246454}; // no warning/ error although it should definitely lose precision
float f2 {static_cast<double>(std::numeric_limits<float>::max()) + 1.0}; // no warning/ error
float f3 {3.5e38}; // error: narrowing conversion of '3.5e+38' from 'double' to 'float' inside { } [-Wnarrowing]

Здорово, что компилятор выполняет фактические проверки диапазона, но достаточно ли этого?

Ответы [ 2 ]

4 голосов
/ 29 марта 2019

Предполагая IEEE 754, с плавающей запятой как 32-битный двоичный файл, удваивается как 64-битный двоичный файл.

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

Например, рассмотрим 1.0000000596046447753906250000000000000000000000000001

1.000000059604644775390625 точно представлен как двойное число и находится точно на половине пути между 1,0 и 1,00000011920928955078125, значение наименьшего числа с плавающей запятой больше 1,0. 1.0000000596046447753906250000000000000000000000000001 округляется до 1.00000011920928955078125 при непосредственном преобразовании, поскольку оно больше средней точки. Если он сначала преобразуется в 64-разрядный, округление до ближайшего приводит к средней точке 1.000000059604644775390625, а затем округление до половины даже округляет до 1,0.

3 голосов
/ 29 марта 2019

Ответ Патриции правильный. Но мы обычно не набираем такое число, так что, может быть, это не проблема ... Разве это не происходит с некоторыми более короткими десятичными литералами?

Я иллюстрировал, что однажды в комментариях после этого ответа Подсчитать количество цифр после `.` в числах с плавающей запятой?

Десятичное значение 7.038531e-26 составляет приблизительно 0x1.5C87FAFFFFFFFCE4F6700 ... p-21, ближайший двойной тип равен 0x1.5C87FB0000000p-21, а ближайший с плавающей точкой равен 0x1.5C87FAp-21.

Обратите внимание, что 0x1.5C87FA0000000p-21 является ближайшим двойным значением к 7.038530691851209e-26

Так что да, может быть проблема двойного округления (ошибка округления дважды в одном направлении) с относительно коротким литералом ...

float x = 7.038531e-26f; и float y = 7.038531e-26; должны быть двумя разными числами, если компилятор правильно округляет литералы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...