gpt4 book ai didi

c# - (.1f+.2f==.3f) != (.1f+.2f).Equals(.3f) 为什么?

转载 作者:行者123 更新时间:2023-12-02 02:17:02 28 4
gpt4 key购买 nike

我的问题是 不是 关于 float 精度。这是关于为什么Equals()不同于 == .

我明白为什么 .1f + .2f == .3ffalse (而 .1m + .2m == .3mtrue )。
我明白了 ==是引用和 .Equals()是值(value)比较。 (编辑:我知道还有更多。)

但为什么是(.1f + .2f).Equals(.3f) true , 而 (.1d+.2d).Equals(.3d)还在false ?

 .1f + .2f == .3f;              // false
(.1f + .2f).Equals(.3f); // true
(.1d + .2d).Equals(.3d); // false

最佳答案

这个问题措辞令人困惑。让我们把它分解成许多小问题:

Why is it that one tenth plus two tenths does not always equal three tenths in floating point arithmetic?



让我给你打个比方。假设我们有一个数学系统,其中所有数字都四舍五入到小数点后五位。假设你说:
x = 1.00000 / 3.00000;

您希望 x 为 0.33333,对吗?因为这是我们系统中最接近真实答案的数字。现在假设你说
y = 2.00000 / 3.00000;

您会期望 y 为 0.66667,对吗?因为,这是我们系统中最接近真实答案的数字。 0.66666 比 0.66667 离三分之二更远。

请注意,在第一种情况下我们向下取整,在第二种情况下我们向上取整。

现在当我们说
q = x + x + x + x;
r = y + x + x;
s = y + y;

我们得到什么?如果我们进行精确算术,那么这些显然是四分之三,而且它们都是相等的。但它们并不相等。尽管 1.33333 是我们系统中最接近三分之二的数字,但只有 r 具有该值。

q 是 1.33332——因为 x 有点小,每次加法都会累积这个误差,最终结果有点太小了。同样,s 太大;它是 1.33334,因为 y 有点太大了。 r 得到正确答案,因为 y 过大被 x 过小抵消,结果最终是正确的。

Does the number of places of precision have an effect on the magnitude and direction of the error?



是的;更高的精度使误差的幅度更小,但可以改变计算是否因误差而产生损失或 yield 。例如:
b = 4.00000 / 7.00000;

b 将是 0.57143,它从 0.571428571 的真实值向上取整……如果我们去了八个地方,那将是 0.57142857,它的误差幅度要小得多,但方向相反;它四舍五入。

因为改变精度可以改变每个单独计算中的错误是 yield 还是损失,所以这可以改变给定聚合计算的错误是相互加强还是相互抵消。最终结果是,有时低精度计算比高精度计算更接近“真实”结果,因为在低精度计算中,您很幸运,并且错误在不同的方向。

We would expect that doing a calculation in higher precision always gives an answer closer to the true answer, but this argument shows otherwise. This explains why sometimes a computation in floats gives the "right" answer but a computation in doubles -- which have twice the precision -- gives the "wrong" answer, correct?



是的,这正是您的示例中发生的情况,只是我们有一定数量的二进制精度而不是五位十进制精度。正如三分之一不能用五位(或任何有限数量)的十进制数字准确表示一样,0.1、0.2 和 0.3 也不能用任何有限数量的二进制数字准确表示。有的会四舍五入,有的会四舍五入,添加是否增加误差或抵消误差取决于 的具体细节。多少位二进制数字在每个系统中。也就是说,精度的变化可以改变答案的好坏。通常精度越高,答案越接近真实答案,但并非总是如此。

How can I get accurate decimal arithmetic computations then, if float and double use binary digits?



如果您需要精确的十进制数学,请使用 decimal类型;它使用十进制分数,而不是二进制分数。你付出的代价是它更大更慢。当然,正如我们已经看到的,像三分之一或四分之四这样的分数不会被准确表示。然而,任何实际上是小数的分数都将以零错误表示,最多约 29 位有效数字。

OK, I accept that all floating point schemes introduce inaccuracies due to representation error, and that those inaccuracies can sometimes accumulate or cancel each other out based on the number of bits of precision used in the calculation. Do we at least have the guarantee that those inaccuracies will be consistent?



不,您无法保证 float 或 double 。编译器和运行时都被允许以比规范要求的精度更高的精度执行浮点计算。特别是,允许​​编译器和运行时以 64 位或 80 位或 128 位或他们喜欢的任何大于 32 的位数进行单精度(32 位)算术。

编译器和运行时被允许这样做,但是他们当时感觉是这样。它们不必在机器与机器之间、运行与运行之间保持一致,等等。由于这只能使计算更准确,因此这不被视为错误。这是一个特点。一个特性使得编写行为可预测的程序变得非常困难,但仍然是一个特性。

So that means that calculations performed at compile time, like the literals 0.1 + 0.2, can give different results than the same calculation performed at runtime with variables?



是的。

What about comparing the results of 0.1 + 0.2 == 0.3 to (0.1 + 0.2).Equals(0.3)?



因为第一个是由编译器计算的,第二个是由运行时计算的,我只是说他们可以随意使用比规范要求更高的精度,是的,它们可以给出不同的结果。也许其中一个选择仅以 64 位精度进行计算,而另一个选择 80 位或 128 位精度进行部分或全部计算并得到不同的答案。

So hold up a minute here. You're saying not only that 0.1 + 0.2 == 0.3 can be different than (0.1 + 0.2).Equals(0.3). You're saying that 0.1 + 0.2 == 0.3 can be computed to be true or false entirely at the whim of the compiler. It could produce true on Tuesdays and false on Thursdays, it could produce true on one machine and false on another, it could produce both true and false if the expression appeared twice in the same program. This expression can have either value for any reason whatsoever; the compiler is permitted to be completely unreliable here.



正确的。

通常向 C# 编译器团队报告这种情况的方式是,某人有一些表达式,在 Debug模式下编译时生成 true,在 Release模式下编译时生成 false。这是最常见的情况,因为调试和发布代码生成改变了寄存器分配方案。但是编译器可以用这个表达式做任何它喜欢的事情,只要它选择真或假。 (也就是说,它不会产生编译时错误。)

This is craziness.



正确的。

Who should I blame for this mess?



不是我,那是肯定的。

英特尔决定制造一种浮点数学芯片,在这种芯片中,要获得一致的结果要贵得多。编译器中关于注册哪些操作与保留在堆栈上的操作的小选择可能会导致结果的巨大差异。

How do I ensure consistent results?



使用 decimal类型,正如我之前所说。或者用整数来做所有的数学运算。

I have to use doubles or floats; can I do anything to encourage consistent results?



是的。如果将任何结果存储到任何静态字段、类型为 float 或 double 的类或数组元素的任何实例字段,则保证将其截断回 32 或 64 位精度。 (此保证明确不适用于本地变量或形式参数的存储。)此外,如果您执行运行时转换为 (float)(double)在已经属于该类型的表达式上,编译器将发出特殊代码,强制结果截断,就好像它已分配给字段或数组元素一样。 (在编译时执行的强制转换——即对常量表达式的强制转换——不能保证这样做。)

To clarify that last point: does the C# language specification make those guarantees?



否。运行时保证存储到数组或字段中。 C# 规范不保证身份转换会被截断,但 Microsoft 实现具有回归测试,以确保编译器的每个新版本都具有此行为。

关于这个主题,语言规范必须说的是,可以根据实现的判断以更高的精度执行浮点运算。

关于c# - (.1f+.2f==.3f) != (.1f+.2f).Equals(.3f) 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15117037/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com