在.NET中双倍乘法?

Dan*_*Dan 53 .net c# math

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

double i = 10*0.69;
Run Code Online (Sandbox Code Playgroud)

i是:6.8999999999999995.为什么?

我理解像1/3这样的数字很难用二进制来表示,因为它有无限的重复小数位,但这不是0.69的情况.并且0.69可以很容易地用二进制表示,一个二进制数表示69,另一个表示小数位的位置.

我该如何解决这个问题?使用decimal类型?

Jon*_*eet 156

因为您误解了浮点运算以及数据的存储方式.

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

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

  • 丹:因为"双重"是计算机,因为"我宁愿把它做​​得比做得快." (60认同)
  • @Dan:因为你可能不是*意味着*0.69.您可能意味着任何其他许多值将由相同的位模式表示.如果要表示本质上基本为十进制的值,则应使用`decimal`类型开头.请注意,在这里选择"输出"是一个问题,即如何将特定类型的实例转换为*text*格式......这与首先决定使用哪种类型有些分开. (35认同)
  • 我可能正在展示我的天真,但为什么框架不能解决这个问题并将这个问题隐藏起来并给我正确答案,0.69 !!! (8认同)

Rus*_*een 53

为什么框架不能解决这个问题并将这个问题隐藏起来并给我正确答案,0.69 !!!

停止表现得像一个迪尔伯特经理,并接受计算机,虽然很酷,很棒,有限制.在您的具体情况下,它不仅仅是"隐藏"问题,因为您已经明确告诉它不要.语言(计算机)提供了您没有选择的格式的替代方案.你选择了double,它具有超过十进制的某些优点,以及某些缺点.现在,知道答案,你会感到不安的是,缺点不会神奇地消失.

作为程序员,您有责任隐藏管理者的这种缺点,并且有很多方法可以做到这一点.但是,C#的制造者有责任正确地使浮点工作,并且正确的浮点有时会导致错误的数学运算.

因此我们没有其他数字存储方法,因为我们没有无限位.我们作为程序员的工作是使用有限的资源来制作很酷的事情.他们在那里90%的路程,让火炬回家.

  • @Dan:而且,基本上(我相信),是Decimal所做的.所以是的,你应该使用它,如果你想要一个确切的答案. (18认同)
  • @Dan:小数位存储在二进制中.所以它出现在1/2,1/4,1/8,1/16,1/32等位置之后. (5认同)
  • 诚然,我对此并不十分了解.但我认为必须有一些工作,例如使用一些替代符号,它似乎是数字69,它可以很容易地存储在二进制,以及小数点的位置. (2认同)

Kei*_*ith 15

并且0.69可以很容易地用二进制表示,一个二进制数表示69,另一个表示 小数位的位置.

我认为这是一个常见的错误 - 你正在考虑浮点数,好像它们是基数10(即十进制 - 因此我的重点).

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

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

你的浮动实际上存储为:
68999999999999995 x 2到一个大负数的幂

一旦你习惯了,这并不是一个问题 - 大多数人都知道并期望1/3无法准确地表示为小数或百分比.只是不能用base-2表示的分数是不同的.


Cli*_*ord 11

但为什么框架不能解决这个问题并将这个问题隐藏起来并给我正确答案,0.69!

因为你告诉它使用二进制浮点,并且解决方案是使用十进制浮点,所以你建议框架应该忽略你指定的类型而不是使用十进制,这是非常慢的,因为它没有直接实现在硬件.

更有效的解决方案是不输出表示的完整值并明确指定输出所需的准确度.如果将输出格式化为两位小数,您将看到预期的结果.但是,如果这是一个财务应用程序十进制正是你应该使用的 - 你已经看到超人III(和Office Space)没有你;)

请注意,它只是无限范围的有限近似,它仅仅是十进制精度使用一组不同的近似值.十进制的优点是它会产生与您自己执行计算时相同的近似值.例如,如果你计算了1/3,那么当它"足够好"时你最终会停止写3.


eri*_*len 8

出于同样的原因,在十进制系统三分之一出来作为0.3333333333333333333333333333333333333333333而不是确切分数,其是无限长.


Ric*_*ard 7

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

double i = (double) Decimal.Multiply(10, (Decimal) 0.69);
Run Code Online (Sandbox Code Playgroud)

大家似乎都回答了你的第一个问题,却忽略了第二部分。