在Nullable Floats上添加时的好奇行为

dav*_*ick 12 c#

在添加可空浮动时,我注意到了一些非常奇怪的东西.请使用以下代码:

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);
Run Code Online (Sandbox Code Playgroud)

result6.099999result26.1.我很幸运,有过一次,因为如果我改变值这个跌跌撞撞a,bc行为通常显示正确.其他算术运算符或其他可空值类型也可能发生这种情况,但这是我能够重现的情况.我不明白的是,为什么隐式转换为floatfloat?没有在第一种情况下正常工作.我或许可以理解它是否试图获得一个int值,条件是条件的另一面0,但这似乎不是正在发生的事情.鉴于result对浮动值的某些组合只显示不正确,我假设这是多次转换的某种舍入问题(可能是由于装箱/拆箱或其他东西).

有任何想法吗?

dav*_*ick 4

请参阅@EricLippert 的评论。

任何事情都可以改变结果——让我再次强调,包括月相在内的任何事情都可以改变,无论浮点数是以 32 位精度还是更高精度计算的。处理器总是可以出于任何原因决定突然开始以 80 位或 128 位或其选择的任何方式进行浮点运算,只要它大于或等于 32 位精度即可。请参阅 (.1f+.2f==.3f) != (.1f+.2f).Equals(.3f) 为什么? 更多细节。

询问在这种情况下具体是什么原因导致处理器决定在一种情况下使用更高的精度,而在另一种情况下则不使用,这是一场失败的游戏。它可以是任何东西如果您需要以十进制数字进行 精确计算,请使用适当命名的类型。如果您需要浮点数中的可重复计算,那么 C# 有两种机制可以强制处理器返回 32 位。(1) 不必要地显式转换为 (float),或 (2) 将结果存储在引用类型的 float 数组元素或 float 字段中。decimal

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

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

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