Я заметил что-то очень странное при работе с добавлением обнуляемых поплавков. Возьмем следующий код:Любопытное поведение при выполнении добавления на неустойчивых поплавках
float? a = 2.1f;
float? b = 3.8f;
float? c = 0.2f;
float? result =
(a == null ? 0 : a)
+ (b == null ? 0 : b)
+ (c == null ? 0 : c);
float? result2 =
(a == null ? 0 : a.Value)
+ (b == null ? 0 : b.Value)
+ (c == null ? 0 : c.Value);
result
является 6.099999
тогда result2
является 6.1
. Мне повезло, что я наткнулся на это, потому что, если я изменил значения для a
, b
и c
, поведение, как правило, выглядит правильным. Это также может случиться с другими арифметическими операторами или другими типами значений NULL, но это случай, когда я смог воспроизвести. Я не понимаю, почему неявный листинг в float
от float?
не работал правильно в первом случае. Я мог бы понять, попытался ли он получить значение int
, учитывая, что другая сторона условного номера равна 0
, но это не похоже на то, что происходит. Учитывая, что result
отображается некорректно для некоторых комбинаций значений с плавающей запятой, я предполагаю, что это какая-то проблема округления с несколькими преобразованиями (возможно, из-за бокса/распаковки или чего-то еще).
Любые идеи?
Просто, чтобы вы знали, [floats не являются точными значениями в компьютерах] (http://floating-point-gui.de/) – Default
http://stackoverflow.com/questions/6683059/are-floating -point-numbers-compatible-in-c-can-they-be – Donal
Я предполагаю, что разница заключается в «значениях в регистре» и «значениях в стеке»; регистры ** намного шире **, чем те же значения в стеке (в данном случае 80 бит вместо 32). Если компилятор может сделать все, что нужно для записи в регистре, он может сохранить большую точность. Если он скомпилирует его для использования стека: гораздо меньше. Но: вы всегда должны ожидать округления с плавающими точками. –