如果始终使用一种 IEEE-754 二进制浮点格式(例如,基本的 32 位二进制,C++ 常用的格式float
),所有 C++ 运算符都以直接和简单的方式映射到 IEEE-754 运算,则lerp_symmetric(alpha, x0, x1)
(以下简称A
) 等于lerp_symmetric(1-alpha, x1, x0)
( B
)
证明:
- 如果
alpha
我们假设在 [0, 1] 中的 大于或等于 ½,则1-alpha
根据 Sterbenz 引理是精确的。(“精确”是指计算的浮点结果等于数学结果;没有舍入误差。)然后,在计算A
时,w0
因为它是1-alpha
,所以w1
是精确的,因为它的数学结果是alpha
,所以它是精确的有代表性的。并且,在计算中B
,w0
因为它的数学结果是精确的alpha
,并且w1
因为它再次是精确的,所以它是精确的1-alpha
。
- 如果
alpha
小于 ½,则1-alpha
可能有一些舍入误差。让结果是beta
。那么,在 中A
,w0
是beta
。现在 ½ ≤ beta
,所以 Sterbenz 引理适用于 的评估w1 = 1.0f - w0
,所以w1
是精确的(并且等于 的数学结果1-beta
)。并且,在 中B
,w0
是精确的,同样由 Sterbenz 引理,并且等于w1
,A
并且w1
(of B
) 是精确的,因为它的数学结果是beta
,它是完全可表示的。
现在我们可以看到w0
inA
等于w1
inB
和w1
inA
等于w0
in B
。在上述任何一种情况下都设beta
为,因此分别返回和。IEEE-754 加法是可交换的(NaN 有效载荷除外),因此返回相同的结果。1-alpha
A
B
(1-beta)*x0 + beta*x1
beta*x1 + (1-beta)*x0
A
B
回答问题:
我会说这是一个合理的方法。我不会断言没有进一步思考就无法做出改进。
不,你不能相信你的编译器:
- C++ 允许实现在评估浮点运算时使用超额精度。因此,即使所有操作数都是 ,
w0*x0 + w1*x1
也可以使用double
,或其他精度进行评估。long double
float
- C++ 允许收缩,除非禁用,因此
w0*x0 + w1*x1
可以评估为fmaf(w0, x0, w1*x1)
,因此对其中一个乘法使用精确算术,但对另一个不使用。
您可以使用以下方法部分解决此问题:
float w0 = 1.0f - alpha;
float w1 = 1.0f - w0;
float t0 = w0*x0;
float t1 = w1*x1;
return t0+t1;
C++ 标准要求在赋值和强制转换中丢弃多余的精度。这扩展到函数返回。(我从内存中报告了这个和其他 C++ 规范;应该检查标准。)因此,float
即使最初使用了额外的精度,上述每一项都会将其结果四舍五入。这将防止收缩。
(还应该能够通过包含<cmath>
和插入预处理器指令来禁用收缩#pragma STDC FP_CONTRACT off
。一些编译器可能不支持。)
上述解决方法的一个问题是,值首先四舍五入到评估精度,然后四舍五入到float
. 有一些数学值,对于这样的值x ,首先将 x 舍入到(或double
另一个精度)然后产生与将x直接float
舍入到 不同的结果。Samuel A. Figueroa del Cid的论文A Rigrous Framework for Fully Supporting the IEEE Standard for Floating-Point Arithmetic in High-Level Programming Languages确定了评估 IEEE-754 基本 64 位浮点中的单次乘法或加法运算(通常用于float
double
) 然后舍入到 32 位格式永远不会出现双舍入错误(因为这些操作,给定的输入是 32 位格式的元素,永远不会产生上述麻烦的x值之一)。1
如果我对从内存报告的 C++ 规范是正确的,那么只要 C++ 实现使用标称格式或足够宽的格式来评估浮点表达式以满足 Figueroa del Cid 给出的要求,那么上述解决方法应该是完整的.
脚注
1根据 Figueroa del Cid,如果x
和y
具有p位有效数,并且x+y
orx*y
被精确计算然后四舍五入到q位,则第二次四舍五入到p位将得到相同的答案,就好像结果直接四舍五入到p位如果p ≤ ( q - 1 )/2。这对于 IEEE-754 基本 32 位二进制浮点 ( p = 24) 和 64 位 ( q = 53) 是满足的。这些格式通常用于float
and double
,上面的解决方法在使用它们的 C++ 实现中应该足够了。如果评估了 C++ 实现float
使用不满足 Figueroa del Cid 给出的条件的精度,则可能会出现双舍入误差。