奇怪的行为与浮动和字符串转换 [英] Strange behaviour with floats and string conversion

查看:158
本文介绍了奇怪的行为与浮动和字符串转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

 >>> 0.1 * 0.1 
0.010000000000000002

我预计0.1 * 0.1不是0.01,因为我知道基数10中的0.1是基数2中的周期性的。

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

已经看到了20个以上的字符。为什么我得到4?

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

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

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

为什么 str round但是 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; //无符号整数32位类型(在每台机器上)
float f; //你想用
玩的类型;

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);



$ b我得到了:

$ b $ b

  0.010000 
0.010000
0.000100

Python给了我:

 >>> 0.01000000000001 
0.010000000000009999
>>> 0.01000000000000002
0.010000000000000019
>>>为什么Python给我这些结果?

$

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

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



在Python 2.x中(2.7之前) repr 通过执行 printf 格式为%。17g ,并丢弃结尾的零。 IEEE-754保证这是正确的(对于64位浮点数)。从2.7和3.1开始,Python使用更智能的算法,在%。17g 给出不必要的非零终端数字或终端9的情况下,可以找到更短的表示。请参阅 3.1中的新增功能 repr(0.1 * 0.1)也是如此, code>给出0.010000000000000002。这是因为在IEEE-754解析和算法下, 0.1 * 0.1 == 0.01 False 也就是说,最接近的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,这是不可循环的,但在许多情况下产生更多的可读结果。


I've typed this into python shell:

>>> 0.1*0.1
0.010000000000000002

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

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

>>> str(0.1*0.1)
'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'

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

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

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

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

I got:

0.010000
0.010000
0.000100

Python gives me:

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

Why does Python give me these results?

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

解决方案

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

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.

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

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.

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆