Python和一般情况下的浮点相等
问题描述
我有一段代码的行为不同,具体取决于我是通过字典获取转换因子还是直接使用它们.
I have a piece of code that behaves differently depending on whether I go through a dictionary to get conversion factors or whether I use them directly.
以下代码将打印 1.0 == 1.0 ->错误
但如果您将 factors[units_from]
替换为 10.0
并将 factors[units_to ]
替换为 1.0/2.54
它将打印 1.0 == 1.0 ->是的
But if you replace factors[units_from]
with 10.0
and factors[units_to ]
with 1.0 / 2.54
it will print 1.0 == 1.0 -> True
#!/usr/bin/env python
base = 'cm'
factors = {
'cm' : 1.0,
'mm' : 10.0,
'm' : 0.01,
'km' : 1.0e-5,
'in' : 1.0 / 2.54,
'ft' : 1.0 / 2.54 / 12.0,
'yd' : 1.0 / 2.54 / 12.0 / 3.0,
'mile' : 1.0 / 2.54 / 12.0 / 5280,
'lightyear' : 1.0 / 2.54 / 12.0 / 5280 / 5.87849981e12,
}
# convert 25.4 mm to inches
val = 25.4
units_from = 'mm'
units_to = 'in'
base_value = val / factors[units_from]
ret = base_value * factors[units_to ]
print ret, '==', 1.0, '->', ret == 1.0
首先让我说我很确定这里发生了什么.我以前在 C 中见过它,只是在 Python 中从未见过,但自从 Python 在 C 中实现后,我们就看到了.
Let me first say that I am pretty sure what is going on here. I have seen it before in C, just never in Python but since Python in implemented in C we're seeing it.
我知道浮点数会改变从 CPU 寄存器到缓存并返回的值.我知道,如果其中一个被分页而另一个驻留在寄存器中,那么比较应该是两个相等的变量将返回 false.
I know that floating point numbers will change values going from a CPU register to cache and back. I know that comparing what should be two equal variables will return false if one of them was paged out while the other stayed resident in a register.
问题
- 避免此类问题的最佳方法是什么?...在 Python 中或一般情况下.
- 我做错了什么吗?
旁注
这显然是一个精简示例的一部分,但我想要做的是提供长度、体积等类别,可以与同一类别但具有不同单位的其他对象进行比较.
This is obviously part of a stripped down example but what I'm trying to do is come with with classes of length, volume, etc that can compare against other objects of the same class but with different units.
反问句
- 如果这是一个潜在的危险问题,因为它会使程序以不确定的方式运行,编译器在检测到您正在检查浮点数的相等性时是否应该发出警告或错误
- 编译器是否应支持将所有浮点相等检查替换为足够接近"函数的选项?
- 编译器是否已经这样做了,我只是找不到信息.
解决方案
感谢您的回复.大多数都非常好,并提供了很好的链接,所以我只会这么说并回答我自己的问题.
Thanks for your responses. Most were very good and provided good links so I'll just say that and answer my own question.
Caspin 发布了这个 链接一个>.
Caspin posted this link.
他还提到谷歌测试使用 ULP 比较,当我查看谷歌代码时,我看到他们提到了与 cygnus-software 相同的确切链接.
He also mentioned that Google Tests used ULP comparison and when I looked at the google code I saw that they mentioned the same exact link to cygnus-software.
我最终用 C 语言实现了一些算法作为 Python 扩展,后来发现我也可以用纯 Python 来实现.代码贴在下面.
I wound up implementing some of the algorithms in C as a Python extension and then later found that I could do it in pure Python as well. The code is posted below.
最后,我可能只是将 ULP 差异添加到我的技巧包中.
In the end, I will probably just wind up adding ULP differences to my bag of tricks.
有趣的是,在两个应该永远不会离开内存的相等数字之间有多少浮点数.我读过的一篇文章或谷歌代码说 4 是一个不错的数字……但在这里我可以达到 10.
It was interesting to see how many floating points are between what should be two equal numbers that never left memory. One of the articles or the google code I read said that 4 was a good number... but here I was able to hit 10.
>>> f1 = 25.4
>>> f2 = f1
>>>
>>> for i in xrange(1, 11):
... f2 /= 10.0 # to cm
... f2 *= (1.0 / 2.54) # to in
... f2 *= 25.4 # back to mm
... print 'after %2d loops there are %2d doubles between them' % (i, dulpdiff(f1, f2))
...
after 1 loops there are 1 doubles between them
after 2 loops there are 2 doubles between them
after 3 loops there are 3 doubles between them
after 4 loops there are 4 doubles between them
after 5 loops there are 6 doubles between them
after 6 loops there are 7 doubles between them
after 7 loops there are 8 doubles between them
after 8 loops there are 10 doubles between them
after 9 loops there are 10 doubles between them
after 10 loops there are 10 doubles between them
<小时>
同样有趣的是,当将其中一个作为字符串写出并读回时,相等数字之间有多少个浮点.
Also interesting is how many floating points there are between equal numbers when one of them is written out as a string and read back in.
>>> # 0 degrees Fahrenheit is -32 / 1.8 degrees Celsius
... f = -32 / 1.8
>>> s = str(f)
>>> s
'-17.7777777778'
>>> # floats between them...
... fulpdiff(f, float(s))
0
>>> # doubles between them...
... dulpdiff(f, float(s))
6255L
<小时>
import struct
from functools import partial
# (c) 2010 Eric L. Frederich
#
# Python implementation of algorithms detailed here...
# from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
def c_mem_cast(x, f=None, t=None):
'''
do a c-style memory cast
In Python...
x = 12.34
y = c_mem_cast(x, 'd', 'l')
... should be equivilent to the following in c...
double x = 12.34;
long y = *(long*)&x;
'''
return struct.unpack(t, struct.pack(f, x))[0]
dbl_to_lng = partial(c_mem_cast, f='d', t='l')
lng_to_dbl = partial(c_mem_cast, f='l', t='d')
flt_to_int = partial(c_mem_cast, f='f', t='i')
int_to_flt = partial(c_mem_cast, f='i', t='f')
def ulp_diff_maker(converter, negative_zero):
'''
Getting the ulp difference of floats and doubles is similar.
Only difference if the offset and converter.
'''
def the_diff(a, b):
# Make a integer lexicographically ordered as a twos-complement int
ai = converter(a)
if ai < 0:
ai = negative_zero - ai
# Make b integer lexicographically ordered as a twos-complement int
bi = converter(b)
if bi < 0:
bi = negative_zero - bi
return abs(ai - bi)
return the_diff
# double ULP difference
dulpdiff = ulp_diff_maker(dbl_to_lng, 0x8000000000000000)
# float ULP difference
fulpdiff = ulp_diff_maker(flt_to_int, 0x80000000 )
# default to double ULP difference
ulpdiff = dulpdiff
ulpdiff.__doc__ = '''
Get the number of doubles between two doubles.
'''
相关文章