【问题标题】:Why does (int)(33.46639 * 1000000) return 33466389?为什么 (int)(33.46639 * 1000000) 返回 33466389?
【发布时间】:2011-01-26 04:39:06
【问题描述】:

(int)(33.46639 * 1000000) 返回33466389

为什么会这样?

【问题讨论】:

  • 您希望发生什么?
  • @Slaks: 3346639,也就是正确答案。
  • @Reverend Gonzo:你不是说“33466390”吗?
  • 如果您告诉我们您使用的语言会有所帮助。您可能使用的语言将 33.46639 视为浮点类型,而不是十进制类型。
  • @Peter,有人删除了c#标签,不知道为什么。

标签: c# floating-point floating-accuracy multiplication


【解决方案1】:

因为 33.46639 不能精确地用有限个二进制数字表示。 33.46639 * 1000000 的实际结果是 33466389.9999999962747097015380859375。演员表将其截断为 33466389。

【讨论】:

  • 试试“不能用有限个二进制小数位精确表示”
【解决方案2】:

那是 1994 年底的新年前夜。英特尔首席执行官安迪·格鲁夫 (Andy Grove) 迎来了美好的一年,奔腾处理器问世并大受欢迎。于是,他走进一家酒吧,点了两杯尊尼获加绿牌。

调酒师端上来说:“先生,那是 20 美元。”

格鲁夫将一张二十美元的钞票放在柜台上,看了一会儿,说:“留下零钱。”

http://en.wikipedia.org/wiki/Pentium_FDIV_bug

【讨论】:

    【解决方案3】:

    双精度并不精确,因此内部 33.46639 实际上存储为 33.466389

    编辑:正如理查德所说,它是浮点数据,(以二进制形式存储在一组有限的位中)所以不完全是这样)......

    【讨论】:

    • 或 33.4668885 或其他“足够接近”的东西,取决于硬件。
    • 嗯,以上都不是。它以 2 为底数。大多数此类数字不能以 10 为底数精确表示。(无论如何,无需使用无限重复的数字序列。类似于 1/3 必须以 10 为底数表示为 0.33333[inf]。)
    • @Richard:错误任何以 2 为底的数字都可以精确地表示为以十为底的非重复小数。 (因为十是二的倍数)
    • FWIW,正是33.46638999999999697365637985058128833770751953125
    • @Richard Berg:大多数实数不能用浮点数表示。所有可以用小数点(二进制?)右边的N位精确表示的数字都可以用小数点右边的N位精确表示。
    【解决方案4】:

    如果你问为什么它没有变成33466390,那是因为doubles 没有无限精度,而且这个数字不能用二进制精确表示。

    如果将double 替换为decimal ((int)(33.46639m * 1000000)),则等于33466390,因为decimals 是以10 为底计算的。

    【讨论】:

    • 此类问题是浮点固有的,而不仅仅是二进制浮点。当然,十进制得到 33.46639*1000000 正确,但仍然有 1/3*3 != 1 和 pow(sqrt(2), 2) != 2。
    • 是的,但他的具体问题是由于二进制。
    【解决方案5】:

    浮点数学并不完美。 What every programmer should know 关于它。

    浮点运算被许多人认为是一门深奥的学科。这是相当令人惊讶的,因为浮点在计算机系统中无处不在。几乎每种语言都有浮点数据类型;从个人电脑到超级计算机的计算机都有浮点加速器;大多数编译器将不时被要求编译浮点算法;几乎每个操作系统都必须响应浮点异常,例如溢出。本文介绍了对计算机系统设计者有直接影响的浮点方面的教程。它从浮点表示和舍入误差的背景开始,继续讨论 IEEE 浮点标准,最后以大量示例说明计算机制造商如何更好地支持浮点。

    ...

    将无限多个实数压缩为有限位数需要近似表示。尽管整数有无限多,但在大多数程序中,整数计算的结果可以存储在 32 位中。相反,给定任何固定位数,大多数实数计算将产生无法使用那么多位精确表示的量。因此,浮点计算的结果必须经常四舍五入以适应其有限表示。这种舍入误差是浮点计算的特征。

    【讨论】:

    • 浮点运算确实无处不在而且很复杂,但这并不能回答这个问题(除非你将链接到某处有答案的 80 页论文)。
    • @Henry - 重点在链接文章的标题中。 每个程序员都应该知道这一点,如果他们不知道,他们应该阅读这篇文章。 (好吧,也许不是全部 80 页......)
    • +1:链接到在某处有答案的 80 页论文是标准答案。这个问题——以一种或另一种形式——被问得太频繁了。这篇论文是的答案。这些问题都是重复的。我们不需要一次又一次地重复这些信息。
    【解决方案6】:

    原因是 33.46639 将被表示为略小于该数字的值。

    乘以 1000000 将得到 33466389.99999999。

    使用 (int) 进行类型转换将只返回整数部分 (33466389)。

    如果您想要“正确”的数字,请在类型转换之前尝试 round()。

    【讨论】:

    • 哇! .....不不不不不。如果你想要“正确”的答案,你就不能使用浮点运算。
    • 没有。如果你想要“正确”的答案,你不能使用 binary 浮点运算。使用 decimal 类型,它使用十进制浮点运算,它会按您的预期工作。
    • 33.46639 是“正确”的答案。问题是提问者没有提出正确的问题。
    • @gabe:我假设的是 C,而不是 C#。从问题上看不清楚。
    • decimal 类型也有问题,尽管是其他数字。任何“位”数固定的数系只能表示有限的一组数,而任意两个数之间有无穷多个实数。
    【解决方案7】:

    你得到不同结果的原因是你使用了'cast'

    (int)(33.46639 * 1000000) 返回 33466389 ^^^^^

    将结果转换为“int”类型...当它们相乘时向上或向下舍入整数类型,然后转换为“int”......不要依赖浮点来足够准确....Skeet 在他的网站herehere 上发布了精彩的介绍...

    【讨论】:

    • 演员阵容本身并没有什么损失。存在任意精度的库。
    • 我想他在问“我的结果中的 8 是从哪里来的?”乘以 1000000 就像将小数点向右移动 6 位,应该是“33466390”,但这不是他得到的。你的答案是我最初的想法,直到我再次阅读这个问题。
    • @Richard - 将 floatdouble 值强制转换为 int 会丢弃小数部分,因此您确实会丢失信息。
    • 赛斯,没错。我要说的是,截断是浮点/双精度的一个特殊功能(通过 CLI 规范)。正如 Tommie 所暗示的那样,这不是 C# 强制转换运算符固有的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-24
    • 2019-10-12
    • 2017-06-12
    • 2012-04-20
    • 1970-01-01
    • 1970-01-01
    • 2020-10-31
    相关资源
    最近更新 更多