Страницы

Поиск по вопросам

понедельник, 22 октября 2018 г.

Приведение double к float в C++

Пусть переменная типа double приводится к переменной типа float. Если переменная double хранит значение NaN, то оно преобразуется в NaN. Если переменная double хранит значение Inf, то оно преобразуется в Inf. Если переменная double хранит значение -1e+50, то оно преобразуется в -Inf
Такое поведение гарантируется стандартом C++, или IEEE 754? Или это implementation-defined behavior, или это вообще undefined behaviour и рассчитывать на то, что -1e+50 преобразуется в -Inf вообще говоря не стоит?


Ответ

Строго говоря, стандарт не определяет размерности float и double, но в своём ответе я буду считать, что они разной размерности и представляют собой 32-х и 64-х разрядные типы данных соответственно.
Т.к. float может вмещать в себя числа от ±1.18×10^(−38) до ±3.4×10^38, а Вы пытаетесь вместить в переменную float, переменную типа double, значение которой не может быть представлено во float в точности, Вы получаете -inf. Вы получаете неопределённое поведение. Поэтому надеяться на -inf не стоит.
Первоначальный мой ответ, утверждающий, что получится неопределённое поведение, является неверным. Неопределённое поведение получается, если мы пытаемся поместить во float такое число, которое не может быть в нём представлено. Вот как это описано в стандарте:
Стандарт C++14 [conv.double]
A prvalue of floating point type can be converted to a prvalue of another floating point type. If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values. Otherwise, the behavior is undefined.
Но это всё касается абстрактной реализации, а у нас она вполне конкретная (и в этом крылась моя ошибка — я примерил конкретную реализацию к абстрактному описанию и сделал ошибочный вывод). Раз уж я сделал предположение о диапазоне допустимых значений float, я взял конкретную реализацию — IEEE 754. А раз я взял эту реализацию, то и отталкиваться нужно от неё, а не от абстракции.
Согласно IEEE 754 бесконечность является частью типа, а следовательно его максимальные и минимальные значения не являются его ограничителями. Просто всё, что лежит вне допустимого диапазона приводится к нужному значению согласно правилам округления. Так вот, согласно этому правилу, если пытаться представить -1e+50 во float, то получится -INF — это нормальное значение float и оно гарантировано при std::numeric_limits::is_iec559 равным true*. Т.е. мы имеем эту ситуацию из стандарта: «the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values»
Если же вышеозначенная константа равна false, то ответ на вопрос может быть другим, но рассмотрен он тоже должен быть в рамках конкретной реализации, т.к. у нас есть конкретная цифра в вопросе, которую мы не можем рассматривать без конкретики со стороны реализации.
Подводя итог: является ли подобное поведение, неопределённым поведением, зависит ли оно от реализации — всё это зависит от представление типов с плавающей точкой и не может быть рассмотрено вне оного.
* Я не нашёл текста самого документа, но многие документы, ссылающиеся на оный, утверждают это.

В языке C это, кстати, указано более явно: если среда поддерживает бесконечность, то результат определён и с примером из вопроса даст гарантировано -inf. Это описано в стандарте C11 [5.2.4.2.2/p5]
The minimum range of representable values for a floating type is the most negative finite floating-point number representable in that type through the most positive finite floating point number representable in that type. In addition, if negative infinity is representable in a type, the range of that type is extended to all negative real numbers; likewise, if positive infinity is representable in a type, the range of that type is extended to all positive real numbers.

Комментариев нет:

Отправить комментарий