【问题标题】:Why do programming languages round down until .6?为什么编程语言要四舍五入到 0.6?
【发布时间】:2011-01-02 03:06:28
【问题描述】:

如果您以必须四舍五入到最接近的第 10 位的格式输入小数,并且它是:1.55,它将四舍五入到 1.5。然后 1.56 将四舍五入为 1.6。我记得在学校里,当你达到 5 时,你会四舍五入,如果是 4 或以下,则向下舍入。为什么它在 Python 等中有所不同。

这是 Python 2.6x 的代码示例(无论最新版本是什么)

'{0:01.2f}'.format(5.555)  # This will return '5.55'

在尝试了提供的一些示例后,我意识到一些更令人困惑的事情:

'{0:01.1f}'.format(5.55)  # This will return '5.5'
# But then
'{0:01.1f}'.format(1.55)  # This will return '1.6'

为什么使用 1.55 和 5.55 时会有差异。两者都被输入为文字(所以浮动)

【问题讨论】:

  • 我不知道有任何计算机语言可以做到这一点。手头有发生这种情况的示例代码吗?
  • 那是因为“1.55”在您的系统上转换为可表示的二进制浮点数只是比十进制值1.55,所以它向上取整; "5.55" 转换为仅小于 小于 5.55 的浮点数,因此向下舍入。

标签: math language-agnostic floating-point rounding


【解决方案1】:

有很多方法可以对数字进行四舍五入。 You can read more about rounding on Wikipedia。 Python 中使用的舍入方法是从零开始舍入一半,您描述的舍入方法或多或少是相同的(至少对于正数而言)。

【讨论】:

  • 问题不是说“Python 等”。 在做回合一半吗?我认为问题的重点是 Round half up 是最常见的方法,而提问者没有看到这种行为。尽管根据我的经验,Python 和其他语言确实使用 Round half up,这就是我感到困惑的原因。
【解决方案2】:

你能否给出一些示例代码,因为这不是我在 Python 中看到的行为:

>>> "%.1f" % 1.54
'1.5'
>>> "%.1f" % 1.55
'1.6'
>>> "%.1f" % 1.56
'1.6'

【讨论】:

    【解决方案3】:

    情况似乎并非如此。您使用的是“float”字符串格式化程序,对吧?

    >>> "%0.2f" % 1.55
    '1.55'
    >>> "%0.1f" % 1.55
    '1.6'
    >>> "%0.0f" % 1.55
    '2'
    

    【讨论】:

      【解决方案4】:

      每种编程语言的舍入和截断都不同,因此您的问题可能与 Python 直接相关。

      但是,rounding 作为一种实践取决于您的方法。

      您还应该知道,在许多编程语言中将小数转换为整数会产生与实际四舍五入不同的结果。

      编辑:根据其他一些海报,Python 似乎没有表现出您所描述的舍入行为:

      >>> "%0.2f" % 1.55 
      '1.55' 
      >>> "%0.1f" % 1.55 
      '1.6' 
      >>> "%0.0f" % 1.55 
      '2' 
      

      【讨论】:

      • 很奇怪,如果你有 %0.2f 并且你去 1.555,你会得到 1.55,但如果你有 %0.1f 和 1.55,你会得到 1.6。如此不一致。
      • 这是因为“1.555”,四舍五入为二进制浮点数,与 1.55 相比,仅在小数点 1.555 的 另一 侧。
      • @orokusaki:浮点数的一致性在它们的上下文中是绝对的,即以 2 为底的算术。以 10 为底的算术在整数的上下文中一对一地映射到以 2 为底的映射,但在涉及分数时却很少。例如,0.125、0.25、0.375、0.5、0.625、0.75 和 0.875 可以精确地以 2 为底表示,因为它们是 2^-1、2^-2 和/或 2^-3 组合的总和。不一致......不是真的。
      【解决方案5】:

      首先,在大多数语言中,像“1.55”这样的未修饰常量被视为双精度值。但是,1.55 不能完全表示为双精度值,因为它没有二进制的终止表示。这会导致许多奇怪的行为,但一种影响是,当您输入 1.55 时,您实际上并没有得到恰好介于 1.5 和 1.6 之间的值。

      在二进制中,十进制数 1.55 是:

      1.10001100110011001100110011001100110011001100110011001100110011001100...
      

      当您键入“1.55”时,该值实际上会四舍五入到最接近的可表示的双精度值(在许多系统上......但也有例外,我会谈到)。这个值是:

      1.1000110011001100110011001100110011001100110011001101
      

      比1.55略;十进制,就是:

      1.5500000000000000444089209850062616169452667236328125
      

      因此,当要求将此值四舍五入到小数点后一位数时,它会将向上四舍五入为 1.6。这就是为什么大多数评论者都说他们无法复制您所看到的行为。

      但是等一下,在您的系统上,“1.55”向下取整,而不是向上取整。怎么回事?

      这可能是一些不同的事情,但最有可能的是您在一个平台(可能是 Windows)上,该平台默认使用 x87 指令进行浮点运算,该指令使用不同的(80 位)内部格式。在 80 位格式中,1.55 的值为:

      1.100011001100110011001100110011001100110011001100110011001100110
      

      比1.55略;在十进制中,这个数字是:

      1.54999999999999999995663191310057982263970188796520233154296875
      

      因为它刚好小于 1.55,所以当它四舍五入到小数点后一位时,它会向下舍入 ,给出您正在观察的结果“1.5”。

      FWIW:在大多数编程语言中,默认的舍入模式实际上是“四舍五入到最接近,与偶数相等”。只是当您以十进制指定小数值时,您几乎永远不会遇到精确的中途情况,因此外行人很难观察到这一点。但是,如果您查看“1.5”如何四舍五入为零位,您可以看到它:

      >>> "%.0f" % 0.5
      '0'
      >>> "%.0f" % 1.5
      '2'
      

      请注意,这两个值都舍入到 偶数 个数字;都不四舍五入为“1”。

      编辑:在您修改后的问题中,您似乎已切换到不同的 python 解释器,在其上以 IEEE754 双精度类型完成浮点,而不是 x87 80 位类型。因此,“1.55”向上取整,就像在我的第一个示例中一样,但“5.55”转换为以下二进制浮点值:

      101.10001100110011001100110011001100110011001100110011
      

      这正是:

      5.54999999999999982236431605997495353221893310546875
      

      十进制;因为它小于小于 5.55,所以它向下舍入

      【讨论】:

      • "%.0f" % 0.5 在 Python 2.5 上为我返回 '1'
      • Dave:有趣——在 Python 2.5.1 上,它也为我返回 '0'。你在哪个平台上?
      • 我相信python“%.0f”只是调用了你系统的C库版本printf。 C 标准建议但不要求 printf 为 printf 浮点格式化程序实现 IEEE754 正确舍入。您可能在图书馆提供者选择不这样做的系统上。
      • 我的措辞也增加了一些混乱。我说的是“十进制格式”,但我正在使用的实际类型是浮点数。这是因为我只是在解释器中输入文字而不是 Decimal('1.55'),所以是的,它被表示为浮点数。
      • @Ken - "%0.f" % 0.5 在 Windows XP 上的 ActivePython 2.5.4 和 Windows 7 上的 ActivePython 2.6.4 中都返回“1”。
      【解决方案6】:

      我看不出您所描述的确切行为的原因。如果您的数字只是示例,则使用银行家四舍五入可以解释类似的情况:

      1.5 rounds to 2
      2.5 rounds to 2
      3.5 rounds to 4
      4.5 rounds to 4
      

      即.5 值将四舍五入到最接近的偶数。这样做的原因是,从长远来看,对大量数字进行四舍五入会变得更平。例如,如果一家银行要向 100 万客户支付利息,其中 10% 的客户最终以 0.5 美分的值四舍五入,如果数值被四舍五入,银行将多支付 500 美元。

      意外舍入的另一个原因是浮点数的精度。大多数数字无法准确表示,因此它们由最接近的可能近似值表示。当你认为你有一个 1.55 的数字时,你实际上可能会得到一个像 1.54999 这样的数字。将该数字四舍五入到小数点后当然会得到 1.5 而不是 1.6。

      【讨论】:

      • 你说 3.5 轮到 2 和 5.5 轮到 4。这是一个错字吧?
      【解决方案7】:

      消除舍入问题的至少一个方面(至少在某些时候)的一种方法是进行一些预处理。单精度和双精度格式可以分别表示从 -2^24-1 到 2^24-1 和 -2^53-1 到 2^53-1 的所有整数。用实数(非零小数部分)可以做的是

      1. 撕下标志并保留以备后用
      2. 将剩余的正数乘以 10^(所需的小数位数)
      3. 如果您的环境的舍入模式设置为截断(向零舍入),则添加 0.5
      4. 将数字四舍五入到最接近的位置
      5. sprintf 将数字转换为格式为 0 位小数的字符串
      6. “手动”根据 sprintf 后面的长度、所需的小数位数、小数点和符号来格式化字符串
      7. 字符串现在应该包含确切的数字

      请记住,如果第 3 步之后的结果超出特定格式的范围(如上),您的答案将不正确。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多