为什么编程语言要向下舍入到 0.6?

发布于 2024-08-16 23:48:04 字数 482 浏览 7 评论 0原文

如果您以必须四舍五入到最接近的十位的格式输入小数,并且它是: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 时会有差异。两者都以文字形式输入(因此是浮动的)

If you put a decimal in a format where has to be rounded to the nearest 10th, and it is: 1.55, it'll round to 1.5. 1.56 will then round to 1.6. In school I recall learning that you round up when you reach five, and down if it's 4 or below. Why is it different in Python, et al.

Here's a code example for Python 2.6x (whatever the latest version is)

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

After trying some of the examples provided, I realized something even more confusing:

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

Why the difference when using 1.55 vs 5.55. Both are typed as literals (so floats)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

离线来电— 2024-08-23 23:48:04

首先,在大多数语言中,像“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,因此向下舍入

First off, in most languages an undecorated constant like "1.55" is treated as a double precision value. However, 1.55 is not exactly representable as double precision value, because it doesn't have a terminating representation in binary. This causes many curious behaviors, but one effect is that when you type 1.55, you don't actually get the value that's exactly halfway between 1.5 and 1.6.

In binary, the decimal number 1.55 is:

1.10001100110011001100110011001100110011001100110011001100110011001100...

When you type "1.55", this value actually gets rounded to the nearest representable double-precision value (on many systems... but there are exceptions, which I'll get to). This value is:

1.1000110011001100110011001100110011001100110011001101

which is slightly larger than 1.55; in decimal, it's exactly:

1.5500000000000000444089209850062616169452667236328125

So, when asked to round this value to a single digit after the decimal place, it will round up to 1.6. This is why most of the commenters have said that they can't duplicate the behavior that you're seeing.

But wait, on your system, "1.55" rounded down, not up. What's going on?

It could be a few different things, but the most likely is that you're on a platform (probably Windows), that defaults to doing floating-point arithmetic using x87 instructions, which use a different (80-bit) internal format. In the 80-bit format, 1.55 has the value:

1.100011001100110011001100110011001100110011001100110011001100110

which is slightly smaller than 1.55; in decimal, this number is:

1.54999999999999999995663191310057982263970188796520233154296875

Because it is just smaller than 1.55, it rounds down when it is rounded to one digit after the decimal point, giving the result "1.5" that you're observing.

FWIW: in most programming languages, the default rounding mode is actually "round to nearest, ties to even". It's just that when you specify fractional values in decimal, you'll almost never hit an exact halfway case, so it can be hard for a layperson to observe this. You can see it, though, if you look at how "1.5" is rounded to zero digits:

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

Note that both values round to even numbers; neither rounds to "1".

Edit: in your revised question, you seem to have switched to a different python interpreter, on which floating-point is done in the IEEE754 double type, not the x87 80bit type. Thus, "1.55" rounds up, as in my first example, but "5.55" converts to the following binary floating-point value:

101.10001100110011001100110011001100110011001100110011

which is exactly:

5.54999999999999982236431605997495353221893310546875

in decimal; since this is smaller than 5.55, it rounds down.

流星番茄 2024-08-23 23:48:04

对数字进行四舍五入的方法有很多种。 您可以在 Wikipedia 上阅读有关舍入的更多信息。 Python 中使用的舍入方法是从零开始舍入一半,并且您描述的舍入方法或多或少是相同的(至少对于正数)。

There are many ways to round numbers. You can read more about rounding on Wikipedia. The rounding method used in Python is Round half away from zero and the rounding method you are describing is more or less the same (at least for positive numbers).

清浅ˋ旧时光 2024-08-23 23:48:04

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

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

Can you give some example code, because that's not the behaviour I see in Python:

>>> "%.1f" % 1.54
'1.5'
>>> "%.1f" % 1.55
'1.6'
>>> "%.1f" % 1.56
'1.6'
冷情妓 2024-08-23 23:48:04

事实似乎并非如此。您正在使用“float”字符串格式化程序,对吗?

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

This doesn't appear to be the case. You're using the "float" string formatter, right?

>>> "%0.2f" % 1.55
'1.55'
>>> "%0.1f" % 1.55
'1.6'
>>> "%0.0f" % 1.55
'2'
故事灯 2024-08-23 23:48:04

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

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

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

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

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

Rounding and truncation is different for every programming language, so your question is probably directly related to Python.

However, rounding as a practice depends on your methodology.

You also should know that CONVERTING a decimal to a whole number in many programming languages yields different results from actually rounding the number.

Edit: Per some of the other posters, it seems that Python does not exhibit the rounding behavior you've described:

>>> "%0.2f" % 1.55 
'1.55' 
>>> "%0.1f" % 1.55 
'1.6' 
>>> "%0.0f" % 1.55 
'2' 
垂暮老矣 2024-08-23 23:48:04

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

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

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

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

I can't see a reason for the exact behaviour that you are describing. If your numbers are just examples, a similar scenario can be explained by bankers rounding being used:

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

I.e. a .5 value will be rounded to the nearest even whole number. The reason for this is that rounding a lot of numbers would even out in the long run. If a bank for example is to pay interrest to a million customers, and 10% of them ends up with a .5 cent value to be rounded, the bank would pay out $500 more if the values were rounded up instead.

Another reason for unexpected rounding is the precision of floating point numbers. Most numbers can't be represented exactly, so they are represented by the closest possible approximation. When you think that you have a number that is 1.55, you may actually ending up with a number like 1.54999. Rounding that number to one decimal would of course result in 1.5 rather than 1.6.

莫多说 2024-08-23 23:48:04

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

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

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

One method to do away with at least one aspect of rounding problems (at least some of the time) is to do some preprocessing. Single and double precision formats can represent all integers exactly from -2^24-1 to 2^24-1 and -2^53-1 to 2^53-1 respectively. What can be done with a real number (with a non-zero fraction part) is to

  1. strip off the sign and keep it for later
  2. multiply the remaining positive number with 10^(number of decimal places required)
  3. add 0.5 if your environment's rounding mode is set to chop (round towards zero)
  4. round the number to nearest
  5. sprintf the number to a string with 0 decimals in format
  6. "manually" format the string according to its length following the sprintf, number of decimal places required, decimal point and sign
  7. the string should now contain the exact number

Keep in mind that if the result after step 3 exceeds the range of the specific format (above) your answer will be incorrect.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文