Когда преобразование целых чисел в числа с плавающей запятой происходит без потерь? - PullRequest
3 голосов
/ 04 августа 2020

Особенно меня интересует, всегда ли int32_t преобразуется без потерь в double.

Всегда ли следующий код возвращает true ?

int is_lossless(int32_t i)
{
    double   d = i;
    int32_t i2 = d;
    return (i2 == i);
}

Что означает int64_t?

Ответы [ 4 ]

5 голосов
/ 04 августа 2020

Когда преобразование целого числа в плавающее выполняется без потерь?

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

Всегда ли следующий код int32_t возвращает истину? -> Да. Всегда ли следующий код int64_t возвращает истину? -> Нет.

Так как DBL_MAX не меньше 1E + 37, то диапазон достаточен как минимум для int122_t, давайте посмотрим на точность.

С обычным double, с его основанием 2, знаковым битом, 53-битным значащим и показателем, все значения int54_t с его 53 битами значений могут быть представлены точно. INT54_MIN также можно представить. С этим double он имеет DBL_MANT_DIG == 53 и в данном случае это количество цифр с основанием 2 в мантиссе с плавающей запятой.

Наименьшее непредставимое значение величины будет INT54_MAX + 2. Тип int55_t и шире имеет значения, которые нельзя точно представить как double.

Для типов uintN_t имеется еще 1 бит значения. Типичный double может затем кодировать все uint53_t и более узкие.

С другими возможными кодировками double, поскольку C указывает DBL_DIG >= 10, все значения int34_t могут проходить туда и обратно.

Код всегда истинен с int32_t, независимо от double кодировка.

Что означает int64_t?

UB-потенциал с int64_t.

Преобразование в int64_t i ... double d = i;, если оно неточно, соответствует определенной реализации результат 2-х ближайших кандидатов. Часто это округление от до ближайшего . Тогда i значения около INT64_MAX могут преобразоваться в double на единицу больше, чем INT64_MAX.

С int64_t i2 = d;, преобразование значения double на единицу больше INT64_MAX в int64_t - это неопределенное поведение (UB).

Простой предварительный тест для обнаружения этого:

#define INT64_MAX_P1 ((INT64_MAX/2 + 1) * 2.0)
if (d == INT64_MAX_P1) return false;  // not lossless
2 голосов
/ 04 августа 2020

Примечание: мой ответ предполагает, что double соответствует IEEE 754, и оба int32_t и int64_t являются дополнением до 2.

Всегда ли следующий код возвращает истину?

мантисса / мантисса double длиннее 32b, поэтому int32_t => double всегда выполняется без ошибок, потому что нет возможной ошибки точности (и нет возможного переполнения / потери значимости, покрытие экспоненты больше необходимого диапазона значений)

Что для int64_t?

, но 53 бита мантиссы / мантиссы (включая 1 неявный) из double не достаточно, чтобы сохранить 64b из int64_t => int64_t с достаточно удаленными верхними и нижними битами, которые нельзя сохранить в double без ошибки точности (все еще нет возможного переполнения / потери значимости, показатель степени все еще покрывает больше, чем необходимо диапазон значений)

1 голос
/ 04 августа 2020

Если ваша платформа использует IEEE754 для double, тогда да, любой int32_t может быть идеально представлен в double. Это не случай для всех возможных значений, которые может иметь int64_t.

(На некоторых платформах возможно настроить размеры мантиссы / экспоненты для типов с плавающей запятой, чтобы преобразование с потерями, но такой тип не будет IEEE754 double.)

Чтобы проверить IEEE754, используйте

static_assert(std::numeric_limits<double>::is_iec559, "IEEE 754 floating point");
0 голосов
/ 05 августа 2020

Вопрос: Всегда ли следующий код возвращает истину?

Всегда является большим заявлением, поэтому ответ будет нет .

Стандарт C ++ не упоминает, относятся ли типы с плавающей запятой, известные C ++ (float, double и long double), к типу IEEE-754. В стандарте явно указано:

Существует три типа с плавающей запятой: float, double и long double. Тип double обеспечивает по крайней мере такую ​​же точность, как float, а тип long double обеспечивает по крайней мере такую ​​же точность, как double. Набор значений типа float является подмножеством набора значений типа double; набор значений типа double является подмножеством набора значений типа long double. Представление значений типов с плавающей запятой определяется реализацией. [Примечание: этот документ не предъявляет требований к точности операций с плавающей запятой; см. также [support.limits]. - конец примечания] Целочисленные типы и типы с плавающей запятой вместе называются арифметическими c типами. Специализации шаблона стандартной библиотеки std​::​numeric_­limits должны указывать максимальное и минимальное значения каждого типа arithmeti c для реализации.

source: Стандарт C ++: basi c основы

Чаще всего тип double представляет двоичный формат с плавающей запятой двойной точности IEEE 754 binary64 и может быть изображенным как:

enter image description here

and decoded as:

enter image description here

However, there is a plethora of other форматы с плавающей запятой , которые декодируются по-разному и не обязательно имеют те же свойства, что и хорошо известный IEEE-754. Тем не менее, они в целом похожи:

  • Они имеют длину n бит
  • Один бит представляет знак
  • m биты представляют собой значащие со скрытым первым битом или без него
  • e биты представляют некоторую форму экспоненты данного основания (2 или 10)

Чтобы узнать, может ли double представлять все 32-битные целые числа со знаком или нет, вы должны ответить на следующий вопрос (при условии, что наше число с плавающей запятой находится в базе 2):

  1. Имеет ли мое представление с плавающей запятой скрытый первый бит в значащем? Если это так, предположим, что m = m + 1
  2. 32-битное целое число со знаком представлено 1 битом знака и 31 битом, представляющим число. Достаточно ли велико значащее, чтобы вместить этот 31 бит?
  3. Достаточно ли велик показатель степени, чтобы он мог представлять число в форме 1.xxxxx 2 ^ 31?

Если можете ответьте да на два последних вопроса, тогда да, int32 всегда может быть представлено double, которое реализовано в этой конкретной системе.

Примечание: Я проигнорировал decimal32 и decimal64 числа, так как я ничего о них не знаю.

...