IEEE 754浮点数加法和乘法的可互换性

6
在IEEE 754(IEC 559)浮点标准中,加法x + x是否可以与乘法2 * x互换?更一般地说,是否有保证case_add和case_mul始终给出完全相同的结果?
#include <limits>

template <typename T>
T case_add(T x, size_t n)
{
    static_assert(std::numeric_limits<T>::is_iec559, "invalid type");

    T result(x);

    for (size_t i = 1; i < n; ++i)
    {
        result += x;
    }

    return result;
}

template <typename T>
T case_mul(T x, size_t n)
{
    static_assert(std::numeric_limits<T>::is_iec559, "invalid type");

    return x * static_cast<T>(n);
}

请注意,似乎有许多方法可以求n*x的和,但令人惊讶的是这么多方法都是等效的!这与https://dev59.com/znzaa4cB1Zd3GeqPOlft和https://dev59.com/Xnzaa4cB1Zd3GeqPOVHA有些关联。 - aka.nice
4个回答

9

在IEEE 754(IEC 559)浮点标准中,加法 x + x 是否可以用乘法 2 * x 互换?

是的,因为它们在数学上是相同的,它们将给出相同的结果(因为在浮点运算中结果是精确的)。

更一般地说,是否有保证 case_add 和 case_mul 总是给出完全相同的结果?

一般来说不是这样。从我所了解的情况来看,它似乎对于 n <= 5 成立:

  • n=3:由于 x+x 是精确的(即不涉及舍入),因此 (x+x)+x 只涉及最后一步的一次舍入。
  • n=4(并且您使用默认的舍入模式),则

    • 如果 x 的最后一位是0,则 x+x+x 是精确的,因此结果相等,原理与 n=3 相同。
    • 如果最后2位是 01,则 x+x+x 的精确值将具有最后2位为 1|1(其中 | 表示格式中的最后一位),将四舍五入为 0|0。下一个加法将给出一个精确结果 |01,因此结果将被向下舍入,抵消先前的误差。
    • 如果最后2位是 11,则 x+x+x 的精确值将具有最后2位为 0|1,将向下舍入为 0|0。下一个加法将给出一个精确结果 |11,因此结果将被向上舍入,再次抵消先前的误差。
  • n=5(同样,假设使用默认舍入):由于 x+x+x+x 是精确的,所以它与 n=3 相同。

对于 n=6,它失败了,例如取 x1.00000000000000021.0 后面的下一个双精度浮点数),在这种情况下,6x6.000000000000002,而 x+x+x+x+x+x6.000000000000001


哇,你的证明比我的证明短多了,只需分析3x是否与2x或4*x在同一二进制位中。 - Pascal Cuoq
1
根据我对IEEE-754 binary32(= float)进行的详尽测试,当FE_TONEAREST时,当n≤5时,乘以n与*(n-1)重复相加是相同的;当FE_TOWARDZERO时,当n≤3时,乘以n(n-1)重复相加是相同的;当FE_DOWNWARD时,当n≤3时,乘以n(n-1)重复相加是相同的;当FE_UPWARD时,当n≤3时,乘以n(n-1)*重复相加是相同的。这个证明是否可以推广到有向舍入模式? - njuffa
1
@njuffa 当 n<=3 时的参数与舍入模式的选择无关。在 n=4 步骤中,证明变得依赖于舍入模式。 - Patricia Shanahan
@PatriciaShanahan 感谢您指出这一点,我忽略了答案已经解决了舍入模式的问题。 - njuffa
如果答案能包括 @njuffa 提到的非默认舍入模式的情况,那就太棒了。 - plasmacel

3
如果n例如是pow(2, 54),那么乘法将正常工作,但在加法路径中,一旦结果值比输入的x足够大,result += x将产生result

1
是的,但这并不普遍适用。乘以一个大于2的数可能不会得到相同的结果,因为您已经改变了指数,并且可以通过替换添加操作来删除一位。然而,如果使用加法操作替换乘以二,则无法删除一位。

1
如果在“case_add”中的累加器“result”变得太大,添加“x”将引入舍入误差。在某个时刻,添加“x”将不起作用。因此,这些函数将不会给出相同的结果。
例如,如果使用十六进制浮点表示法定义“double x = 0x1.0000000000001p0”:
n  case_add              case_mul

1  0x1.0000000000001p+0  0x1.0000000000001p+0
2  0x1.0000000000001p+1  0x1.0000000000001p+1
3  0x1.8000000000002p+1  0x1.8000000000002p+1
4  0x1.0000000000001p+2  0x1.0000000000001p+2
5  0x1.4000000000001p+2  0x1.4000000000001p+2
6  0x1.8000000000001p+2  0x1.8000000000002p+2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接