2015-04-29 3 views
8

Предположим, у меня есть два арифметических типа, целочисленный один, I и с плавающей точкой один, F. Я также предполагаю, что std::numeric_limits<I>::max() меньше, чем std::numeric_limits<F>::max().Является ли округление с плавающей точкой всегда определяемым поведением, если диапазон с плавающей запятой больше?

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

Однако, если у меня есть значение с плавающей запятой f такое, что f == F(i), is I(f) четко определено? Другими словами, всегда задано поведение I(F(i))?


соответствующий раздел из стандарта C++ 14:

4.9 Плавающие-интегральные преобразования[conv.fpint]

  1. prvalue типа с плавающей точкой может быть преобразовано в prvalue целочисленного типа. Преобразование усекает; , то есть дробная часть отбрасывается. Поведение не определено, если усеченное значение не может быть , представленное в типе назначения. [Примечание: Если тип адресата bool, см. 4.12. - конец примечания]
  2. Предел целочисленного типа или типа перечислимого типа не может быть преобразован в значение значения плавающего типа . Результат является точным, если это возможно. Если преобразованное значение находится в диапазоне значений, которые могут быть представлены , но значение не может быть представлено точно, это выбор, определенный реализацией либо следующим нижним, либо более высоким представляемым значением. [Примечание: Потеря точности происходит, если интегральное значение не может быть представлено точно как значение плавающего типа. - конец примечания] Если преобразованное значение находится вне диапазон значений, которые могут быть представлены, поведение не определено. Если тип источника bool, значение false преобразуется в ноль, а значение true преобразуется в единицу.
+0

@vsoftco Вам придется перефразировать это. 'I <= i' не имеет смысла - вы сравниваете тип со значением. – orlp

+2

[Нет] (http://coliru.stacked-crooked.com/a/e58cd5864d532045). –

+2

Представьте, что 'I' и' F' имеют одинаковый размер, скажем 32 бита. Затем возьмем наибольшее целое число. Он обязательно преобразует с потерей значения в значение типа 'F'. Если выбранное значение, которое выбрано, больше, чем следующее целое число, то преобразование результатов обратно в UB. –

ответ

-1

No.

Вполне возможно, что i == std::numeric_limits<I>::max(), но i не точно представима в F.

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

Поскольку следующий более высокий представима значение может быть выбрано, то не исключено, что в результате F(i) больше не вписывается в I, так что преобразование обратно будет неопределенное поведение.

+0

Downvoter, позаботьтесь, чтобы объяснить? – orlp

4

Однако, если у меня есть значение с плавающей запятой f таким образом, что f == F(i), является I(f) хорошо определены? Другими словами, всегда задано поведение I(F(i))?

No.

Предположит, что I является знаковым дополнением до двух 32-битный целый типа, F является 32 битами одинарной точности с плавающей точкой типа, и i максимальным положительным целое число. Это находится в пределах диапазона типа с плавающей точкой, но его нельзя представить точно как число с плавающей запятой. Некоторые из этих 32 бит используются для экспоненты.

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

+0

Незначительный нитпик. _ "Вместо этого преобразование из целых чисел в раунды с плавающей точкой до ближайшего представимого значения." _ Это не гарантируется. Это будет смежное представление, а не самое близкое. – orlp

+0

С технической точки зрения, это не неопределенное поведение, а реализация определенного поведения (поскольку это зависит от семантики используемых типов с плавающей запятой, которые определены для реализации). – Wug

+0

@Wug Реализация определена, если она будет неопределенной или нет :) – orlp

-1

No. Независимо от стандарта, вы не можете ожидать, что в общем случае это преобразование вернет исходное целое число. Это не имеет смысла математически. Но если вы читаете то, что вы цитируете, стандарт четко указывает на возможность потери точности при преобразовании из int в float.

Предположим, что ваши типы I и F используют одинаковое количество бит. Все биты I (за исключением, возможно, того, который хранит знак), используются для указания абсолютного значения числа. С другой стороны, в F некоторые биты используются для указания экспоненты, а некоторые используются для значимости. Диапазон будет больше из-за возможного показателя. Но значение будет иметь меньшую точность, потому что для его спецификации меньше бит.

Подобно тому, как испытание, я напечатал

std::numeric_limits<int>::max(); 
std::numeric_limits<float>::max(); 

тогда я преобразовал первый номер, чтобы плавать и обратно. Максимальный поплавок имел показатель 38, а максимальный int имел 10 цифр, поэтому четкое плавание имеет больший диапазон. Но после преобразования max int в float и back, я пошел от 2147473647 до -2147473648. Кажется, что число увеличилось на одну единицу и обошло стороной отрицательную сторону.

Я не проверял, сколько бит фактически используется для float в моей системе, но, по крайней мере, демонстрирует потерю точности, и это показывает, что gcc «округляется».

+0

Я никогда не запрашивал первоначальное целочисленное значение. Я просто спросил, было ли преобразование определено поведение. – orlp

+0

Извините, что вы больше не читали свой вопрос. –