小编典典

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

python

我已经将其输入python shell:

>>> 0.1*0.1
0.010000000000000002

我期望0.1 * 0.1不是0.01,因为我知道以10为底的0.1是周期性的,以2为底。

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

我已经看过20个以上的字符,因此我希望能得到20个。为什么我得到4?

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

好吧,这解释了为什么我len给了我4,但为什么str回归'0.01'

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

为什么不str回合repr?(我已经阅读了这个答案,但是我想知道他们如何决定何时str对浮点数进行取整,以及何时对不进行浮点取整)

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

因此,浮子的准确性似乎是一个问题。我以为Python会使用IEEE 754单精度浮点数。所以我已经检查过了:

#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\n", testVar.f);

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

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

我有:

0.010000
0.010000
0.000100

Python给了我:

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

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

(我使用的是Python 2.6.5。如果您知道Python版本之间的差异,我也会对此感兴趣。)


阅读 165

收藏
2020-12-20

共1个答案

小编典典

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

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

即使在Python 2.7下,也会repr(0.1 * 0.1)给出"0.010000000000000002"。这是因为0.1 * 0.1 == 0.01False在IEEE-754解析和算法;
也就是说,与之最接近的64位浮点值与0.1自身相乘时,将得出一个64位的浮点值,该值不是与之最接近的64位浮点值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

repr和之间str(2.7 /
3.1之前的版本)的区别在于,str格式具有12位小数而不是17位,这是不可四舍五入的,但是在许多情况下产生的可读性更高。

2020-12-20