C# int хранится в double "= = " проблема точности
вот упрощенный код:
int i = 1;
double a = i;
double b = i;
это гарантирует, что a == b
is правда?
4 ответов
гарантируется ли, что 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, но я сомневаюсь, что вы будете использовать эти большие числа.