在float16 Numpy数字上执行数学运算时,结果也是float16类型数。 我的问题是如何计算结果? 假设我乘以/添加两个float16数字,python是否在float32中生成结果然后将结果截断/舍入为float16?或者一直在'16位多路复用器/加法器硬件'中进行计算?
另一个问题 - 是否有float8类型?我找不到这个......如果没有,那么为什么?谢谢大家!
答案 0 :(得分:11)
对于第一个问题:在典型处理器(至少在GPU之外)上没有float16
的硬件支持。 NumPy完全按照您的建议执行操作:将float16
个操作数转换为float32
,对float32
值执行标量操作,然后将float32
结果舍入为float16
}。可以证明结果仍然是正确的:float32
的精度足够大(相对于float16
的精度),这里双舍入不是问题,至少对于四基本算术运算和平方根。
在当前的NumPy源代码中,这就是float16
标量运算的四种基本算术运算的定义。
#define half_ctype_add(a, b, outp) *(outp) = \
npy_float_to_half(npy_half_to_float(a) + npy_half_to_float(b))
#define half_ctype_subtract(a, b, outp) *(outp) = \
npy_float_to_half(npy_half_to_float(a) - npy_half_to_float(b))
#define half_ctype_multiply(a, b, outp) *(outp) = \
npy_float_to_half(npy_half_to_float(a) * npy_half_to_float(b))
#define half_ctype_divide(a, b, outp) *(outp) = \
npy_float_to_half(npy_half_to_float(a) / npy_half_to_float(b))
上面的代码取自NumPy源中的scalarmath.c.src。您还可以查看loops.c.src以获取数组ufuncs的相应代码。支持npy_half_to_float
和npy_float_to_half
函数在halffloat.c中定义,以及float16
类型的各种其他支持函数。
对于第二个问题:不,NumPy中没有float8
类型。 float16
是标准化类型(在IEEE 754标准中描述),在某些情况下(特别是GPU)已广泛使用。没有IEEE 754 float8
类型,并且似乎没有明显的“标准”float8
类型的候选者。我也猜测在NumPy中对float8
支持的需求并不多。
答案 1 :(得分:2)
这个答案建立在问题的float8
方面。接受的答案很好地涵盖了其余部分。除了缺乏标准之外,没有广泛接受的float8
类型的主要原因之一是它实际上不是非常有用。
在标准表示法中,使用内存中的float[n]
位存储n
数据类型。这意味着最多只能表示2^n
个唯一值。在IEEE 754中,少数这些可能的值(如nan
)不是偶数。这意味着所有浮点表示(即使你去float256
)在它们能够表示的有理数集合中有间隙,如果你试图获得一个数字的表示,它们会舍入到最接近的值间隙。通常,n
越高,这些差距越小。
如果您使用struct
包来获取某些float32
数字的二进制表示,则可以看到操作上的差距。起初遇到它有点令人吃惊,但在整数空间中只有32的差距:
import struct
billion_as_float32 = struct.pack('f', 1000000000 + i)
for i in range(32):
billion_as_float32 == struct.pack('f', 1000000001 + i) // True
通常,浮点最好只跟踪最重要的位,这样如果您的数字具有相同的比例,则保留重要的差异。浮点标准通常仅在它们在基数和指数之间分配可用位的方式上有所不同。例如,IEEE 754 float32
使用24位作为基数,8位作为指数。
float8
通过上面的逻辑,float8
值只能占用256个不同的值,无论你在基数和指数之间分割比特有多聪明。除非你热衷于将数字四舍五入为接近零的256个任意数字之一,否则跟踪int8
中的256种可能性可能更有效。
例如,如果您想要以粗略的精度跟踪非常小的范围,您可以将所需的范围划分为256个点,然后存储您的数字最接近的256个点中的哪一个。如果你想得到真正的幻想,你可能会有一个非线性的值分布,无论是聚集在中心还是边缘,取决于你最重要的事情。
其他人(甚至自己以后)需要这种确切方案的可能性非常小,并且大部分时间你支付的额外字节或3作为使用{{1}的惩罚}或float16
相反,它太小而无法产生有意义的差异。因此......几乎没有人愿意编写float32
实现。