Только a) и b) гарантированно преуспеют в любой вменяемой реализации (подробности см. Ниже), так как они сравнивают два значения, которые были получены одинаковым образом и округлены до точности float
. Следовательно, оба сравниваемых значения гарантированно будут идентичны последнему биту.
Случаи c) и d) могут потерпеть неудачу, потому что вычисление и последующее сравнение могут быть выполнены с большей точностью, чем float
. Различного округления double
должно быть достаточно, чтобы не пройти тест.
Обратите внимание, что случаи a) и b) могут все же потерпеть неудачу, если задействованы бесконечности или NAN.
ЮРИДИЧЕСКАЯ
Используя рабочий проект стандарта N3242 C ++ 11, я обнаружил следующее:
В тексте, описывающем выражение присваивания, прямо указано, что происходит преобразование типов, [expr.ass] 3:
Если левый операнд не относится к типу класса, выражение неявно преобразуется (пункт 4) в cv-неквалифицированный тип левого операнда.
Раздел 4 относится к стандартным преобразованиям [conv], которые содержат следующие преобразования в числах с плавающей запятой, [conv.double] 1:
Значение типа с плавающей запятой может быть преобразовано в значение другого типа с плавающей запятой. Если
исходное значение может быть точно представлено в целевом типе, результат преобразования
представление. Если исходное значение находится между двумя соседними целевыми значениями, результат преобразования
является определенным реализацией выбором любого из этих значений. В противном случае поведение не определено.
(Акцент мой.)
Таким образом, у нас есть гарантия того, что результат преобразования фактически определен, если только мы не имеем дело со значениями за пределами представимого диапазона (например, float a = 1e300
, то есть UB).
Когда люди думают, что «внутреннее представление с плавающей точкой может быть более точным, чем видимое в коде», они думают о следующем предложении в стандарте, [expr] 11:
Значения плавающих операндов и результаты плавающих выражений могут быть представлены в большем
точность и дальность, чем требуется типом; типы не меняются.
Обратите внимание, что это относится к операндам и результатам , а не к переменным. Это подчеркивается прилагаемой сноской 60:
Операторы приведения и присваивания по-прежнему должны выполнять свои конкретные преобразования, как описано в 5.4, 5.2.9 и 5.17.
(Я думаю, это сноска, которую Мачей Пехотка имел в виду в комментариях - нумерация, похоже, изменилась в используемой им версии стандарта.)
Итак, когда я говорю float a = some_double_expression;
, у меня есть гарантия, что результат выражения на самом деле округляется до значения, представляемого float
(вызывая UB, только если значение выходит за пределы), и a
будет ссылаться на это округленное значение впоследствии.
Реализация может действительно указывать, что результат округления является случайным, и, таким образом, разбивать случаи a) и b). Однако здравомыслящие реализации этого не сделают.