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

Strange behaviour with floats and string conversion(浮点数和字符串转换的奇怪行为)
本文介绍了浮点数和字符串转换的奇怪行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

我已经在 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.

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

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

patching a class yields quot;AttributeError: Mock object has no attributequot; when accessing instance attributes(修补类会产生“AttributeError:Mock object has no attribute;访问实例属性时)
How to mock lt;ModelClassgt;.query.filter_by() in Flask-SqlAlchemy(如何在 Flask-SqlAlchemy 中模拟 lt;ModelClassgt;.query.filter_by())
FTPLIB error socket.gaierror: [Errno 8] nodename nor servname provided, or not known(FTPLIB 错误 socket.gaierror: [Errno 8] nodename nor servname provided, or not known)
Weird numpy.sum behavior when adding zeros(添加零时奇怪的 numpy.sum 行为)
Why does the #39;int#39; object is not callable error occur when using the sum() function?(为什么在使用 sum() 函数时会出现 int object is not callable 错误?)
How to sum in pandas by unique index in several columns?(如何通过几列中的唯一索引对 pandas 求和?)