C # int хранится в двойной точности "==" - PullRequest
9 голосов
/ 27 ноября 2010

Вот упрощенный код:

int i = 1;
double a = i;
double b = i;

Гарантируется ли, что a == b равно верно ?

Ответы [ 4 ]

11 голосов
/ 27 ноября 2010

Да. 32-разрядные целые числа могут быть представлены в точности как 64-разрядные числа с плавающей запятой.

9 голосов
/ 27 ноября 2010

Гарантируется ли, что a == b истинно?

Да.Это потому, что вы выполняете одно и то же преобразование дважды и, учитывая его детерминированное поведение, вы получите одинаковые значения независимо от проблем округления.

Мы можем обобщить ваш вопрос, однако:

Можем ли мы выполнить арифметические операции над 32-разрядными целочисленными значениями, закодированными в типе double без потери точности?

Ответ на такой вопрос также положительный.

Короткийоправдание состоит в том, что операции над битами мантиссы (см. http://en.wikipedia.org/wiki/Significand) точны, если это возможно, а в случае 32-разрядных целочисленных значений это возможно.

Более длинная история приходит сюда. Пока вашцелочисленное значение помещается в 52 бита дробной части, называемой мантиссой (см. http://en.wikipedia.org/wiki/Double_precision), все вычисления целых значений с использованием double будут вести себя вполне нормально.

Это потому, что ваше число (скажем, 173, что составляет 0000010101101b двоичный) будет представлен как 1.010110100000b*2^7, что является точным.

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

Возвращение к целым числам, закодированным в операции двойного четного деления, является точным, если в результате получается целочисленное значение.Так что 4.0/2.0 == 8.0/4.0 также гарантированно будет истинным.

Проблема начинается, когда ваш номер не является целым числом.Но даже в этом случае гарантируется, что числа будут точно представлены, если они имеют форму x/2^y и x в 52 бита (например, 3/4 5/8 345/1024).Операции над такими числами также точны, поскольку y может быть одинаковым для обоих операндов, поэтому даже:

123456789/1024/1024/1024/1024 == 
(23456789/1024/1024/1024/1024 +
100000000/1024/1024/1024/1024)

гарантированно будет истинным.

Интересный факт заключается в том, что вы можете выполнять операциюна 54-битных целых чисел со знаком безопасно.Это потому, что у вас есть дополнительный бит в начале, значение которого закодировано экспонентой, и один дополнительный бит для знака.Теперь -2 ^ 53, что будет MIN_INT в случае 54-битного целого числа со знаком, не соответствует мантиссе, но экспонента выполнит эту работу с мантиссой, полной нулей.

3 голосов
/ 27 ноября 2010

Да, вы можете хранить (32-разрядное) целое число в double (64-разрядное число с плавающей запятой) без потери точности.

Однако, как только вы выполните вычисления с вашим double, вы, скорее всего, внесете ошибки округления, то есть потерю точности. Эти ошибки, вероятно, будут достаточно небольшими, чтобы они округлялись, когда вы приводите значение double обратно к int & mdash; но ошибка есть, так что имейте это в виду.

Как это делается: См. этот документ ( Стандарт IEEE 754 числа с плавающей запятой от Стива Холлаша) для получения подробной информации о том, как целое число может быть сохранено как значение с плавающей точкой.

Подводя итог (несколько неточно), значение с плавающей запятой состоит из трех частей: знакового бита, части «дроби» (называемой мантиссой) и части «экспоненты». Они составлены примерно следующим образом:

значение = -1 знаковый бит & times; дробь & раз; 2 показатель степени

Вы можете сохранить целочисленное значение в части «дроби» double (ширина которой составляет 52 бита, что более чем достаточно для 32-разрядного целого числа. Часть «экспонента» можно просто установить на 0, так как он не нужен.

0 голосов
/ 27 ноября 2010

Я открыл Visual Studio и проверил его.

Вот мой код:

int i = 5;
double t = i;
double k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i += 5;
t += 5;
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i += (int)Math.Round(5.6);
t += 5.6;
t = (int)Math.Round(t);
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i = int.MaxValue - 5438;
t = int.MaxValue - 5438;
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true
i = (int)Math.Round(double.MaxValue);
t = Math.Round(double.MaxValue);
k = i;
MessageBox.Show((i == t).ToString()); //false
MessageBox.Show((k == t).ToString()); //false
i = (int)Math.Round(double.MaxValue);
t = i;
k = i;
MessageBox.Show((i == t).ToString()); //true
MessageBox.Show((k == t).ToString()); //true

В результате получилось два сообщения, говорящих true.

Я полагаю, чтоприходит к выводу, что: да, вам гарантировано, что это будет правдой.

РЕДАКТИРОВАТЬ: немного расширил мой тест.Единственным тестом, возвращающим false, был тест в double.MaxValue, но я сомневаюсь, что вы будете использовать эти большие числа.

...