浮点数和字符串转换的奇怪行为

时间:2022-10-19
本文介绍了浮点数和字符串转换的奇怪行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

我已经在 python shell 中输入了这个:

I've typed this into python shell:

>>> 0.1*0.1
0.010000000000000002

我预计 0.1*0.1 不是 0.01,因为我知道以 10 为底的 0.1 在以 2 为底的周期性.

I expected that 0.1*0.1 is not 0.01, because I know that 0.1 in base 10 is periodic in base 2.

>>> len(str(0.1*0.1))
4

我预计会得到 20 个,因为我在上面看到了 20 个字符.为什么我得到 4?

I expected to get 20 as I've seen 20 characters above. Why do I get 4?

>>> str(0.1*0.1)
'0.01'

好的,这就解释了为什么我 len 给我 4,但为什么 str 返回 '0.01'?

Ok, this explains why I len gives me 4, but why does str return '0.01'?

>>> repr(0.1*0.1)
'0.010000000000000002'

为什么 str 圆而 repr 不圆?(我已阅读 this answer,但我想知道他们是如何决定何时 str舍入一个浮点数,当它不舍入时)

Why does str round but repr not? (I have read this answer, but I would like to know how they have decided when str rounds a float and when it doesn't)

>>> str(0.01) == str(0.0100000000001)
False
>>> str(0.01) == str(0.01000000000001)
True

所以浮动的准确性似乎有问题.我认为 Python 会使用 IEEE 754 单精度浮点数.所以我已经这样检查了:

So it seems to be a problem with the accuracy of floats. I thought Python would use IEEE 754 single precicion floats. So I've checked it like this:

#include <stdint.h>
#include <stdio.h> // printf

union myUnion {
    uint32_t i; // unsigned integer 32-bit type (on every machine)
    float f;    // a type you want to play with
};

int main() {
    union myUnion testVar;
    testVar.f = 0.01000000000001f;
    printf("%f
", testVar.f);

    testVar.f = 0.01000000000000002f;
    printf("%f
", testVar.f);

    testVar.f = 0.01f*0.01f;
    printf("%f
", testVar.f);
}

我明白了:

0.010000
0.010000
0.000100

Python 给了我:

Python gives me:

>>> 0.01000000000001
0.010000000000009999
>>> 0.01000000000000002
0.010000000000000019
>>> 0.01*0.01
0.0001

为什么 Python 会给我这些结果?

Why does Python give me these results?

(我使用 Python 2.6.5.如果您知道 Python 版本的差异,我也会对它们感兴趣.)

(I use Python 2.6.5. If you know of differences in the Python versions, I would also be interested in them.)

推荐答案

repr 的关键要求是它应该往返;也就是说,eval(repr(f)) == f 在所有情况下都应该给出 True.

The crucial requirement on repr is that it should round-trip; that is, eval(repr(f)) == f should give True in all cases.

在 Python 2.x(2.7 之前)中,repr 通过使用 %.17g 格式执行 printf 并丢弃尾随零来工作.IEEE-754 保证这是正确的(对于 64 位浮点数).从 2.7 和 3.1 开始,Python 使用了一种更智能的算法,可以在某些情况下找到更短的表示,其中 %.17g 给出了不必要的非零终端数字或终端九.请参阅 3.1 中有哪些新功能? 和 问题 1580.

In Python 2.x (before 2.7) repr works by doing a printf with format %.17g and discarding trailing zeroes. This is guaranteed correct (for 64-bit floats) by IEEE-754. Since 2.7 and 3.1, Python uses a more intelligent algorithm that can find shorter representations in some cases where %.17g gives unnecessary non-zero terminal digits or terminal nines. See What's new in 3.1? and issue 1580.

即使在 Python 2.7 下,repr(0.1 * 0.1) 也会给出 "0.010000000000000002".这是因为0.1 * 0.1 == 0.01在IEEE-754解析和算术下是False;也就是说,最接近 0.1 的 64 位浮点值,当与自身相乘时,会产生一个不是最接近 0.1 的 64 位浮点值的 64 位浮点值代码>0.01:

Even under Python 2.7, repr(0.1 * 0.1) gives "0.010000000000000002". This is because 0.1 * 0.1 == 0.01 is False under IEEE-754 parsing and arithmetic; that is, the nearest 64-bit floating-point value to 0.1, when multiplied by itself, yields a 64-bit floating-point value that is not the nearest 64-bit floating-point value to 0.01:

>>> 0.1.hex()
'0x1.999999999999ap-4'
>>> (0.1 * 0.1).hex()
'0x1.47ae147ae147cp-7'
>>> 0.01.hex()
'0x1.47ae147ae147bp-7'
                 ^ 1 ulp difference

reprstr (pre-2.7/3.1) 的区别在于 str 格式有 12 位小数,而不是 17 位,这是不可往返的,但在许多情况下会产生更具可读性的结果.

The difference between repr and str (pre-2.7/3.1) is that str formats with 12 decimal places as opposed to 17, which is non-round-trippable but produces more readable results in many cases.

这篇关于浮点数和字符串转换的奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一条:浮点数在 Python 中的取值范围是多少? 下一条:将浮点数转换为位置格式的字符串(没有科学记数法和错误精度)

相关文章

最新文章