C# int хранится в double "= = " проблема точности

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

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

это гарантирует, что a == b is правда?

4 ответов


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


гарантируется ли, что A == B истинно?

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

мы можем обобщить ваш вопрос, хотя:

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

в ответ на такой вопрос-да.

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

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

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

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

назад к целым числам, закодированным в операции деления double - even, точно, если результатом является целочисленное значение. Так что 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-битного знака целое число не соответствует мантиссе, но экспонента будет выполнять работу здесь с мантиссой, полной нулей.


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

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

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

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

стоимостью = -1Регистрация бит × фракция × 2показатель

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


Я открыл 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

в результате было два messageboxes говорят правду.

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

EDIT: немного расширил мой тест. Единственным тестом, возвращающим false, был тест double.MaxValue, но я сомневаюсь, что вы будете использовать эти большие числа.