关于C#:在.NET中双倍乘法?

Is double Multiplication Broken in .NET?

本问题已经有最佳答案,请猛点这里访问。

如果我在C中执行以下表达式:

1
double i = 10*0.69;

i为:6.8999999999999995。为什么?

我知道像1/3这样的数字很难用二进制表示,因为它有无限个重复出现的小数位,但这不是0.69的情况。0.69可以很容易地用二进制表示,一个二进制数表示69,另一个二进制数表示小数点的位置。

我怎么解决这个问题?使用decimal类型?


因为你误解了浮点运算和数据的存储方式。

实际上,在这种特殊情况下,您的代码在执行时并没有实际执行任何算术运算——编译器将执行该运算,然后在生成的可执行文件中保存一个常量。但是,它不能存储6.9的精确值,因为该值不能以浮点格式精确表示,就像1/3不能以有限的十进制表示精确存储一样。

看看这篇文章是否对您有所帮助。


why doesn't the framework work around this and hide this problem from me and give me the
right answer,0.69!!!

别再表现得像个呆伯特的管理者了,接受电脑虽然很酷,但也有局限性。在您的具体案例中,它不仅仅是"隐藏"问题,因为您已经明确告诉它不要这样做。语言(计算机)提供了您没有选择的格式的替代方案。您选择了double,它比decimal有一定的优势,也有一定的缺点。现在,知道答案后,你会为自己的缺点不会神奇地消失而心烦意乱。

作为一个程序员,你有责任向管理者隐瞒这个缺点,而且有很多方法可以做到这一点。然而,C的制造商有责任使浮点正确工作,正确的浮点偶尔会导致错误的数学。

其他的数字存储方法也是如此,因为我们没有无限位。作为程序员,我们的工作是使用有限的资源来实现酷的事情。他们把你带到90%的地方,把火炬拿回家。


And 0.69 can easily be represented in
binary, one binary number for 69 and
another to denote the position of the
decimal place.

我认为这是一个常见的错误——你把浮点数看成是以10为基数的数字(即十进制——因此我强调了这一点)。

所以-你认为这个双精度数有两个整数部分:69除以100得到小数点移动-也可以表示为:69 x 10的幂为-2。

然而,浮点数将"点的位置"存储为基2。

您的浮动实际上存储为:68999999999995 x 2为某个大负数的幂

一旦你习惯了这一点,这就不再是一个问题了——大多数人都知道并期望1/3不能准确地表示为小数或百分比。只是不能用基2表示的分数是不同的。


but why doesn't the framework work around this and hide this problem from me and give me the right answer,0.69!!!

因为您告诉它使用二进制浮点数,而解决方案是使用十进制浮点数,所以您建议框架应该忽略您指定的类型,而改用十进制,因为它不是直接在硬件中实现的,所以速度要慢得多。

更有效的解决方案是不输出表示的完整值,并显式指定输出所需的精度。如果将输出格式化为两个小数位,您将看到预期的结果。但是,如果这是一个财务应用程序,小数点正是您应该使用的-您已经看到了超人III(和办公空间),不是吗;)

请注意,它都是无限范围的有限近似值,只是十进制和double使用了一组不同的近似值。小数的优点是它产生的近似值与您自己进行计算时的近似值相同。例如,如果你计算了1/3,你最终会在"足够好"的时候停止写3。


由于同样的原因,十进制中的1/3表示为0.33333333333333333333333333333333333333333333333333333333333333333333,而不是无限长的精确分数。


要解决此问题(例如在屏幕上显示),请尝试以下操作:

1
double i = (double) Decimal.Multiply(10, (Decimal) 0.69);

似乎每个人都回答了你的第一个问题,但忽略了第二部分。