Случаи 0,8-0,7
В 0.8-0.7 == 0.1
ни один из литералов не может быть точно представлен в double
.Ближайшие представимые значения 0,8000000000000000444089209850062616169452667236328125 для .8, 0,6999999999999999555910790149937383830547332763671875 для .7 и 0,10000000000000000555111512312578270201561540451.Когда вычтены первые два, результат равен 0,100000000000000088817841970012523233890533447265625.Поскольку это не равно третьему, 0.8-0.7 == 0.1
оценивается как ложное.
В (float)(0.8-0.7) == (float)(0.1)
результат 0.8-0.7
и 0.1
каждый преобразуется в float
.Ближайшее к первому значение float
, 0,1000000000000000055511151231257827021181583404541015625, равно 0,100000001490116119384765625.Ближайшее к последнему значение float
, 0,100000000000000088817841970012523233890533447265625, равно 0,100000001490116119384765625.Поскольку они одинаковы, (float)(0.8-0.7) == (float)(0.1)
оценивается как true.
В (double)(0.8-0.7) == (double)(0.1)
результат 0.8-0.7
и 0.1
каждый преобразуется в double
.Поскольку они уже double
, эффект отсутствует, и результат такой же, как для 0.8-0.7 == 0.1
.
Примечания
Спецификация C #, версия 5.0 указывает, что float
и double
являются 32-разрядными и 64-разрядными типами IEEE-754 с плавающей точкой.Я не вижу явного утверждения, что они являются двоичными форматами с плавающей запятой, а не десятичными, но описанные характеристики делают это очевидным.В спецификации также указывается, что, как правило, используется арифметика IEEE-754 с округлением до ближайшего (предположительно, округлением до ближайшего связывания с четностью), за исключением нижеприведенного.
Спецификация C # позволяетАрифметика с плавающей точкой должна выполняться с большей точностью, чем номинальный тип.В п. 4.1.6 говорится: «… операции с плавающей точкой могут выполняться с большей точностью, чем тип результата операции…». Это может усложнить анализ выражений с плавающей точкой в целом, но это не касается нас в случае 0.8-0.7 == 0.1
поскольку единственной применимой операцией является вычитание 0.7
из 0.8
, и эти числа находятся в одном и том же бинарном выражении (имеют одинаковую степень двойки в представлении с плавающей запятой), поэтому результат вычитания является точно представимыми дополнительная точность не изменит результат.Пока преобразование исходных текстов 0.8
, 0.7
и 0.1
в double
не использует дополнительную точность, а приведение к float
дает float
без дополнительной точности, результаты будутбыть как указано выше.(Стандарт C # говорит в п. 6.2.1, что преобразование из double
в float
дает значение float
, хотя в нем прямо не говорится, что в этой точке нельзя использовать дополнительную точность.)
Дополнительные кейсы
В 8-0.7 == 7.3
у нас есть 8 для 8
, 7.29999999999999982236431605997495353221893310546875
для 7.3
, 0,6999999999999999555910790149937383830547332763671875 для 0.7
, и 7,299999999999999822364316059974954 560 5997495 545 529, 5275
Обратите внимание, что дополнительная точность, допускаемая спецификацией C #, может повлиять на результат 8-0.7
.Реализация AC #, которая использовала дополнительную точность для этой операции, могла бы привести к ложному результату для этого случая, поскольку она получала бы другой результат для 8-0.7
.
В 18.01-0.7 == 17.31
, у нас есть 18.010000000000001563194018672220408916473388671875 для 18.01
, 0.6999999999999999555910790149937383830547332763671875
для 0.7
, 17.309999999999998721023075631819665431976318359375
для 17.31
и 17.31000000000000227373675443232059478759765625
для 18.01-0.7
, поэтому результат равен false.
Как вычитать 8 отличий от вычитания 18,01, если они обавычитается числом с плавающей запятой?
18,01 больше 8 и требует большей степени двойки в его представлении с плавающей точкой.Аналогично, результат 18.01-0.7
больше, чем 8-0.7
.Это означает, что биты в их значениях (часть дроби представления с плавающей запятой, которая масштабируется степенью двойки) представляют большие значения, в результате чего ошибки округления в операциях с плавающей запятой, как правило, больше.В общем, формат с плавающей запятой имеет фиксированный диапазон - существует фиксированное расстояние от сохраненного старшего бита до сохраненного младшего бита.Когда вы меняете число с большим количеством битов слева (старшие биты), некоторые биты справа (младшие биты) выталкиваются, и результаты меняются.