对可为空的浮点数进行加法时的奇怪行为

本文关键字:浮点数 | 更新日期: 2023-09-27 18:19:56

我在添加可为空的浮点数时注意到一些非常奇怪的事情。采用以下代码:

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);

result6.099999,而result26.1。我很幸运能偶然发现这一点,因为如果我更改ab 的值,并且c行为通常看起来是正确的。其他算术运算符或其他可为 null 的值类型也可能发生这种情况,但这是我能够重现的情况。我不明白的是,为什么在第一种情况下,从float?隐式强制转换为float无法正常工作。我也许可以理解它是否试图获得一个int值,因为条件的另一边是0,但这似乎不是正在发生的事情。鉴于result仅对浮点值的某些组合显得不正确,我假设这是多次转换的某种舍入问题(可能是由于装箱/拆箱或其他原因(。

有什么想法吗?

对可为空的浮点数进行加法时的奇怪行为

查看@EricLippert的评论。

任何事情都可以改变结果——让我强调一下 同样,包括月相在内的任何东西都被允许 更改浮点数是以 32 位精度计算还是更高 准确性。处理器始终被允许出于任何原因 决定突然开始做 80 位的浮点运算,或者 128 位或任何它选择的,只要它大于或等于 32 位精度。看 (.1f+.2f==.3f( != (.1f+.2f(.等于(.3f( 为什么? 了解更多详情。

询问在这种情况下,是什么导致处理器决定 在一种情况下使用更高的精度而不在另一种情况下是失败的 游戏。它可以是任何东西。如果您需要准确的计算然后使用恰如其分命名的decimal类型。如果您需要 浮点数中的可重复计算,则 C# 有两种机制用于 强制处理器恢复为 32 位。(1( 显式转换为(浮点( 不必要地,或 (2( 将结果存储在浮点数组元素中或 引用类型的浮点型字段。

此处的行为与 Nullable 类型无关。这是一个浮点数永远不会精确的问题,并且根据处理器的突发奇想以不同的精度计算。

一般来说,这归结为这样的建议:如果准确性很重要,最好的选择是使用float以外的其他方法(或使用@EricLippert描述的技术强制处理器使用 32 位精度(。

埃里克·利珀特(Eric Lippert(对相关问题的回答也有助于理解正在发生的事情。