为什么repr(float)在Google App Engine上返回的位数比其他数字多

时间:2018-08-15 01:00:13

标签: python google-app-engine floating-point

我有一些简单的代码,如下所示

a = 0.8889
print repr(a)
print str(a)

我在几种不同的系统(包括Python 2.7.12〜2.7.14,作为提示输入或脚本)上进行了尝试,总是得到结果

0.8889
0.8889

这里是one example

但是,在App Engine(运行时Python 2.7.12)上,我得到了

0.88890000000000002
0.8889

我了解repr() trends to be more precise。问题是为什么App Engine的行为会有所不同。是否依赖底层硬件?

背景

这个事实使我很烦,因为在将浮点数转换为json时,我不必要地获得了多余的数字。

更新

事实证明这是GAE运行时的错误。正如Mark所言,根本原因是sys.float_repr_style的配置丢失。 GAE支持团队创建了一个错误报告here

Update2: 自2018/08/19起已修复。即使尚未宣布。

2 个答案:

答案 0 :(得分:1)

很明显,正如@abarnert在评论中提到的那样,GAE env在后台做了一些工作,其中之一是提高了所用浮点数的精度。

首先要注意的事情之一是:

>>> a = 0.88890000000000002
>>> print a
0.8889

这意味着多余的数字是没有用的。

您可以使用Decimal模块来重现这种情况:

>>> from decimal import *
>>> Context(prec=17).create_decimal_from_float(0.899).__str__()
'0.89900000000000002'

有趣的是,GAE似乎试图模拟〜17的浮点精度(float没有特定的十进制精度,因为它们表示为浮点数)。如果> 17,则会得到更多的小数,如果少于,则数字将不够精确。

使用Decimal的优点是浮点错误更少,尽管看起来repr()与此相关。

查看以下更多扩展示例以提供更多上下文:

>>> from decimal import *
>>> Context(prec=16).create_decimal_from_float(0.899).__str__()
'0.8990000000000000'
>>> Context(prec=17).create_decimal_from_float(0.899).__str__()
'0.89900000000000002'
>>> Context(prec=18).create_decimal_from_float(0.899).__str__()
'0.899000000000000021'
>>> repr(float(1.0000000000000003)).__str__()
'1.0000000000000002'
>>> Context(prec=17).create_decimal_from_float(1.0000000000000003).__str__()
'1.0000000000000002'
>>> repr(float(1.0000000000000002)).__str__()
'1.0000000000000002'
>>> Context(prec=17).create_decimal_from_float(1.0000000000000002).__str__()
'1.0000000000000002'
>>> repr(float(1.0000000000000001)).__str__()
'1.0'
>>> Context(prec=17).create_decimal_from_float(1.0000000000000001).__str__()
'1'

最后,由python float 0.899表示的实际数字是:

>>> from decimal import *
>>> Decimal(float(0.899))
Decimal('0.89900000000000002131628207280300557613372802734375')

因此,最后,repr在GAE中提供的表示非常精确。

答案 1 :(得分:1)

sys.float_repr_style的值为'legacy',这是GAE在构建时设置的选项,无法更改。

此版本的repr算法曾经是Python 2.7之前使用的格式,该算法先计算17个有效数字,然后将输出基于这17个数字(适当时将尾随零除掉