IEEE 754 浮点加法和乘法的互换性
Interchangeability of IEEE 754 floating-point addition and multiplication
在 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
例如 pow(2, 54)
那么乘法就可以正常工作,但是在加法路径中,一旦结果值足够大于输入 x
,result += x
将产生 result
.
是的,但它并不普遍。乘以大于 2 的数字可能不会得到相同的结果,因为您已经更改了指数并且如果您替换为加法可能会下降一点。但是,如果用加法运算代替,乘以二不能下降一点。
如果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
Is the addition x + x
interchangeable by the multiplication 2 * x
in IEEE 754 (IEC 559) floating-point standard
是的,因为它们在数学上是相同的,所以它们会给出相同的结果(因为结果是精确的浮点数)。
or more generally speaking is there any guarantee that case_add and case_mul always give exactly the same result?
一般不会,不会。据我所知,它似乎适用于 n <= 5
:
n=3
:因为x+x
是精确的(即不涉及四舍五入),所以(x+x)+x
只涉及最后一步的四舍五入。
n=4
(并且您使用的是默认舍入模式)然后
- 如果
x
的最后一位为 0,则 x+x+x
是准确的,因此结果与 n=3
. 的参数相同
- 如果最后 2 位是
01
,那么 x+x+x
的确切值将具有 1|1
的最后 2 位(其中 | 表示格式中的最后一位),这将四舍五入为 0|0
。下一次相加会给出一个准确的结果 |01
,因此结果会向下舍入,抵消之前的错误。
- 如果最后 2 位是
11
,那么 x+x+x
的精确值将具有 0|1
的最后 2 位,将向下舍入为 0|0
.下一次加法将给出一个准确的结果 |11
,因此结果将向上舍入,再次抵消之前的错误。
n=5
(再次假设默认舍入):因为 x+x+x+x
是精确的,所以它与 n=3
.
的理由相同
对于 n=6
它失败了,例如将 x
设为 1.0000000000000002
(1.0
之后的下一个 double
),在这种情况下 6x
是 6.000000000000002
而 x+x+x+x+x+x
是6.000000000000001
在 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
例如 pow(2, 54)
那么乘法就可以正常工作,但是在加法路径中,一旦结果值足够大于输入 x
,result += x
将产生 result
.
是的,但它并不普遍。乘以大于 2 的数字可能不会得到相同的结果,因为您已经更改了指数并且如果您替换为加法可能会下降一点。但是,如果用加法运算代替,乘以二不能下降一点。
如果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
Is the addition
x + x
interchangeable by the multiplication2 * x
in IEEE 754 (IEC 559) floating-point standard
是的,因为它们在数学上是相同的,所以它们会给出相同的结果(因为结果是精确的浮点数)。
or more generally speaking is there any guarantee that case_add and case_mul always give exactly the same result?
一般不会,不会。据我所知,它似乎适用于 n <= 5
:
n=3
:因为x+x
是精确的(即不涉及四舍五入),所以(x+x)+x
只涉及最后一步的四舍五入。n=4
(并且您使用的是默认舍入模式)然后- 如果
x
的最后一位为 0,则x+x+x
是准确的,因此结果与n=3
. 的参数相同
- 如果最后 2 位是
01
,那么x+x+x
的确切值将具有1|1
的最后 2 位(其中 | 表示格式中的最后一位),这将四舍五入为0|0
。下一次相加会给出一个准确的结果|01
,因此结果会向下舍入,抵消之前的错误。 - 如果最后 2 位是
11
,那么x+x+x
的精确值将具有0|1
的最后 2 位,将向下舍入为0|0
.下一次加法将给出一个准确的结果|11
,因此结果将向上舍入,再次抵消之前的错误。
- 如果
的理由相同n=5
(再次假设默认舍入):因为x+x+x+x
是精确的,所以它与n=3
.
对于 n=6
它失败了,例如将 x
设为 1.0000000000000002
(1.0
之后的下一个 double
),在这种情况下 6x
是 6.000000000000002
而 x+x+x+x+x+x
是6.000000000000001