Numpy's float32 and float comparisons
继续Python float和numpy float32之间的区别:
1 2 3 4 5 6 7 | import numpy as np a = 58682.7578125 print(type(a), a) float_32 = np.float32(a) print(type(float_32), float_32) print(float_32 == a) |
打印:
1 2 3 | <class 'float'> 58682.7578125 <class 'numpy.float32'> 58682.8 True |
我完全理解,比较浮点数是否相等不是一个好主意,但仍然不应该是False(我们在谈论的是第一个十进制数字的差异,而不是0.000000001)?是否依赖系统?是否在某处记录了这种行为?
编辑:好吧,这是第三位小数:
1 2 | print(repr(float_32), repr(a)) # 58682.758 58682.7578125 |
但是我可以信任
EDIT2:人们坚持以更高的精度打印float_32会给我它的表示形式。但是,正如我已经根据nympy的文档评论的那样:
the % formatting operator requires its arguments to be converted to standard python types
和:
1 | print(repr(float(float_32))) |
版画
58682.7578125
@MarkDickinson在这里给出了一个有趣的见解,显然
因此,让我重申如下问题:
-
在示例中,如何获得
float_32 和a 的确切内部表示形式?如果这些相同,那么如果不解决, -
在python的
float 和np.float32 之间进行比较时,上/下转换的确切规则是什么?我猜想它会将float_32转换为float,尽管@WillemVanOnsem在评论中建议相反
我的python版本:
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:18:55) [MSC v.1900 64 bit (AMD64)] on win32
数字之所以相等,是因为可以在32位和64位浮点数中精确表示58682.7578125。让我们仔细看一下二进制表示形式:
1 2 3 4 5 6 7 8 9 | 32 bit: 01000111011001010011101011000010 sign : 0 exponent: 10001110 fraction: 11001010011101011000010 64 bit: 0100000011101100101001110101100001000000000000000000000000000000 sign : 0 exponent: 10000001110 fraction: 1100101001110101100001000000000000000000000000000000 |
它们具有相同的符号,相同的指数和相同的分数-64位表示形式中的多余位用零填充。
无论采用哪种方式进行铸造,它们都将比较相等。如果您尝试使用其他数字,例如58682.7578124,您会发现表示形式在二进制级别上有所不同。 32位失去了更高的精度,它们将无法相等。
(在二进制表示中也很容易看出,可以将float32转换为float64而不会丢失任何信息。这是numpy在比较两者之前应该做的。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import numpy as np a = 58682.7578125 f32 = np.float32(a) f64 = np.float64(a) u32 = np.array(a, dtype=np.float32).view(dtype=np.uint32) u64 = np.array(a, dtype=np.float64).view(dtype=np.uint64) b32 = bin(u32)[2:] b32 = '0' * (32-len(b32)) + b32 # add leading 0s print('32 bit: ', b32) print('sign : ', b32[0]) print('exponent: ', b32[1:9]) print('fraction: ', b32[9:]) print() b64 = bin(u64)[2:] b64 = '0' * (64-len(b64)) + b64 # add leading 0s print('64 bit: ', b64) print('sign : ', b64[0]) print('exponent: ', b64[1:12]) print('fraction: ', b64[12:]) |
他们是平等的。它们只是打印不同,因为它们使用不同的打印逻辑。
How can I get at the exact internal representation of float_32 and a in the example ?
好吧,这取决于您所说的"精确的内部表示形式"。如果确实需要一个位值数组,则可以得到:
1 2 3 4 | >>> b = numpy.float32(a) >>> numpy.unpackbits(numpy.array([b]).view(numpy.uint8)) array([1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1], dtype=uint8) |
它与"精确的内部表示形式"非常接近,但这并不是最有用的东西。 (此外,结果将取决于字节序,因为它实际上是基于原始内部表示形式的。)
如果您想要C级浮点数,这就是NumPy如何在C级上表示float32值的方法……那就是C。除非要编写自己的C扩展模块,否则不能直接使用C级值。您可以得到的最接近的是C浮点数周围的某种包装器,嘿!您已经有一个!但是,您似乎对此并不满意,因此这并不是您真正想要的。
如果要使用以人类可读的十进制表示的确切值,请使用
1 2 3 4 | >>> b 58682.758 >>> decimal.Decimal(float(b)) Decimal('58682.7578125') |
您选择的58682.7578125值恰好可以表示为浮点数,因此,十进制表示形式恰好与您输入的值相同,但通常情况并非如此。您键入的确切十进制表示形式将被丢弃并且无法恢复。
What are the exact rules for up/downcasting in a comparison between python's float and np.float32 ?
float32被无损地转换为float64。
小数点58682.7578125是精确的分数(
分母为2(
因此,Victor T的答案是正确的:在内部表示中,它的值相同。
IMO对相同的值(即使对于不同的类型)都回答正确的事实,这对IMO是件好事,您对
相同的值在内部存储,只显示不带
尝试:
1 | print"%0.8f" % float_32 |
请参阅相关的全精度打印numpy.float64
58682.8
我的机器在这条线上显示58682.758。
I fully understand that comparing floats for equality is not a good idea
如果它们是独立计算的,则"不是一个好主意"。另一方面,如果您获得相同的数字并检查其转换,则是一个好主意。
Is it system dependent ? Is this behavior somewhere documented ?
它完全取决于转换为文本。根据评论,float32是必不可少的。如果是这样,则float32的保证精度为7个十进制数字,这与Python的内部float为float64(至少在x86上)不同。这就是为什么该值在打印中被截断的原因。建议使用十进制格式打印浮点值的方法是在输出形式转换回相同的内部值时停止。因此它将58682.7578125减少为58682.758:差异小于ULP。
与内部" float"或numpy float64打印的相同值将具有更高的有效数字,因为它们的省略将导致另一个内部值:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> 58682.758 == 58682.7578125 False >>> numpy.float32(58682.758) == numpy.float32(58682.7578125) True >>> print(repr(numpy.float32(58682.758).data[0:4])) '\xc2:eG' >>> print(repr(numpy.float32(58682.7578125).data[0:4])) '\xc2:eG' >>> numpy.float64(58682.758) == numpy.float64(58682.7578125) False >>> print(numpy.float64(58682.758).hex(), numpy.float64(58682.7578125).hex()) ('0x1.ca7584189374cp+15', '0x1.ca75840000000p+15') |
您很幸运,这两个值在float32中与此具体值相等(是故意的吗?),但可能与其他值不同。