我试图了解 NumPy 如何实现四舍五入到最接近的数,即使在转换为较低精度格式时也是如此,在本例中为 Float32 到 Float16,具体来说,当数字在 Float32 中是正常的,但在 Float16 中四舍五入为低于正常值时。
我的理解如下,
在 float32 中,数字有位
31 | 三十 | 二十九 | 二十八 | 二十七 | 二十六 | 二十五 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 十三 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
s | e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 | m0 | 米1 | 平方米 | 米3 | 米4 | m5 | 米6 | m7 | M8 | m9 | m10 | m11 | m12 | m13 | m14 | m15 | m16 | m17 | m18 | m19 | m20 | m21 | m22 |
/*
* If the last bit in the half significand is 0 (already even), and
* the remaining bit pattern is 1000...0, then we do not add one
* to the bit after the half significand. However, the (113 - f_exp)
* shift can lose up to 11 bits, so the || checks them in the original.
* In all other cases, we can just add one.
*/
if (((f_sig&0x00003fffu) != 0x00001000u) || (f&0x000007ffu)) {m
f_sig += 0x00001000u;
}
上述代码用于打破最接近偶数的平局。我不明白为什么在逻辑或的第二部分,我们对0x0000'07ffu
(位 m12-m22) 进行按位与,而不是0x0000'ffffu
(m11-m22) 进行按位与。
一旦我们将尾数位对齐为 float16 的亚正规格式(这是此段代码之前的位移所做的),在上面的 float32 数字表示中,我们就可以m10
决定m22
要舍入的方向。
我的理解是,OR 的第二部分检查数字是否大于中间点,如果是,则将半有效数字位加一。但对于原始数字,它不是只检查中间点以上的数字子集吗?在 float16 数字中,m9 将是最后一个要保留的精度。因此,如果满足以下条件,我们将向上舍入:
m9 为 1,m10 为 1,m11-m22 均为 0(或的第一部分)
m10 为 1,m11-m22 中至少有一个为 1(将数字置于中间点以上)
如果 m11-m22 中任何一个为 1,则可以通过将 1 添加到 m10 来简化。如果 m10 已经为 1,则添加将影响到 m9,否则将不受影响。但是,在 NumPy 代码的情况下,检查的位是 m12-m22。
我不确定我遗漏了什么。这是一个特殊情况吗?
我期望位 m11-m22 能够决定是否加 1 以及是否加 m12-m22。
f_sig
包含一个为 binary16 结果准备的有效数字。(binary16是 IEEE-754 中对某些人称之为“半精度”浮点格式的名称。)此时,代码需要位 22:13 中的有效数字位,因为稍后要将它们再移动 13 位,将其放入 9:0。为此做准备,它根据指数移动了位。这会将一些位移出f_sig
。现在它要测试新有效数字的低位(现在在位 13)是否为 0,有效数字以下的最高位(在位 12)是否为 1,并且所有剩余位是否为 0。其中一些剩余位位于 的位 11:0 中
f_sig
。但其中一些可能已经消失。根据指数的移位将其中一些移出。因此,要测试这些位是否为 0,我们在 中的原始有效数字中查看它们f
。由于指数移位最多移出 11 位,我们只需查看 的低 11 位
f
。原始有效数字的其他位仍然存在于 中f_sig
。因此,在 中
(f_sig&0x00003fffu) != 0x00001000u) || (f&0x000007ffu)
, 的左操作数||
测试 中的原始有效位f_sig
,而 的右操作数测试 中的原始有效位f
。可能存在一些重叠;后者可能会测试 中的某些位f_sig
,但这并不重要。不,它没有检查这一点。当且仅当尾部不正好是新有效数字的最低有效位 (LSB) 的 ½ 或最低有效位为 1 时,测试才为真。
理由如下:
f_sig += 0x00001000u;
会添加 ½ LSB,随后在 LSB (f_sig >> 13
) 处截断有效数字。这在大多数情况下可提供所需的舍入:向小于 ½ 的尾随部分添加 ½ 不会进位,向大于 ½ 的尾随部分添加 ½ 会进位。