Python numpy float16 数据类型操作和 float8?

问题描述 投票:0回答:2

当对 float16 Numpy 数字执行数学运算时,结果也是 float16 类型数字。 我的问题是结果到底是如何计算的? 假设我将两个 float16 数字相乘/相加,Python 是否会生成 float32 格式的结果,然后将结果截断/舍入为 float16 格式?还是一直在‘16位多路复用器/加法器硬件’中进行计算?

另一个问题 - 有 float8 类型吗?我找不到这个……如果没有,那为什么?谢谢大家!

python numpy floating-point precision
2个回答
35
投票

对于第一个问题:在典型的处理器上(至少在 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
函数以及 float16 类型的各种其他支持函数在
halffloat.c
中定义。

对于第二个问题:不,NumPy 中没有

float8
类型。
float16
是一种标准化类型(在 IEEE 754 标准中进行了描述),已在某些环境(尤其是 GPU)中广泛使用。没有 IEEE 754
float8
类型,并且似乎没有“标准”
float8
类型的明显候选者。我还猜测 NumPy 中对
float8
支持的需求并没有那么大。


27
投票

这个答案建立在问题的

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 个点中与您的数字最接近的一个。如果您想变得真正奇特,您可以将值的非线性分布聚集在中心或边缘,具体取决于对您最重要的内容。

其他人(甚至后来的你自己)需要这个确切方案的可能性是极其,而且大多数时候,你为使用

float16
float32
而支付的额外字节或3个字节作为惩罚太小了做出有意义的改变。因此......几乎没有人费心编写一个
float8
实现。

© www.soinside.com 2019 - 2024. All rights reserved.