提问人:Martin Ba 提问时间:2/9/2022 最后编辑:Martin Ba 更新时间:2/12/2022 访问量:221
获得两个“接近”IEEE754双精度值之间的“精确”差异?
Get "precise" difference between two "near" IEEE754 double values?
问:
我什至不确定我所要求的是否可能,但这里是:
我们的 C++ 代码执行以下计算:
double get_delta(double lhs, double rhs) {
return lhs - rhs;
}
并且输入(最接近的 double to),这不会产生“最接近”的 double 到 ,而是产生一个更接近 的值。655.36
655.34
0.02
0.019999..
当然,我们不能指望IEEE双精度计算的精确结果,但我想知道是否可以改进朴素的delta计算,以更接近我们的预期:
鉴于两个输入值可以非常精确地表示(15 位数字,见下文),不幸的是,但意料之中的是,差异没有相同的精度(与理想结果相比):
从以下值可以看出,通过相互减去两个值,生成的增量值的有效位数低于两个起始值。
虽然示例值最多精确到 16 位十进制数字(DBL_DIG
毕竟是 15 位),但生成的增量值最多只能精确到 13 位十进制数字。也就是说,第 13 位之后的所有数字都由原始值中数字 16 之后的噪声组成。
因此,在这种情况下,将增量值四舍五入为 13 位有效十进制数字将再次产生“正确”结果,因为它会给我 .0.02d
因此,问题可能就变成了:
假设两个减法值 a - b
都假定为双精度,即 15 位有效十进制数字的精度,如何计算结果差值的精度?
具体而言:
655.36 === 6.55360000000000013642420526594 E2 [v1]
655.34 === 6.55340000000000031832314562052 E2 [v2]
^ ^
1 17
0.02 === 2.00000000000000004163336342344 E-2 [v3]
655.36
- === 1.9999999999981810106 E-2 (as calculated in MSVC 2019) [v4]
655.34
^ ^ ^
1 13 17
根据要求,这里有一个生成和打印相关数字的C++程序:https://gist.github.com/bilbothebaggins/d8a44d38b4b54bfefbb67feb5baad0f5
从 C++ 打印的数字是:
'a'@14: 6.55360000000000e+02
' '@__: ................
'a'@18: 6.553600000000000136e+02
'a'@XX: 0x40847ae147ae147b
'b'@14: 6.55340000000000e+02
' '@__: ................
'b'@18: 6.553400000000000318e+02
'b'@XX: 0x40847ab851eb851f
'd'@14: 1.99999999999818e-02
' '@__: ................
'd'@18: 1.999999999998181011e-02
'd'@XX: 0x3f947ae147ae0000
'2'@14: 2.00000000000000e-02
' '@__: ................
'2'@18: 2.000000000000000042e-02
'2'@XX: 0x3f947ae147ae147b
增量后来被用于一些乘法,这自然会使误差进一步恶化。
有没有办法以更复杂的方式进行增量计算,以便我们更接近“正确”答案?
我目前的想法是:
如果我们要使用无限精度类型进行计算,给定 double 的输入,我们首先必须决定如何将给定的 double 四舍五入为我们的无限精度类型。
比如说,我们四舍五入到 15 位十进制数字(这对于我们用例的输入来说已经足够了),我们将得到精确的值——即 完全。如果我们再计算无限精度增量,我们将得到确切的结果。如果我们将该值转换回 double,我们将得到该值而不是“错误”的值。655.3?
0.02
v3
v4
那么,有没有办法对起始值进行归一化,以便在纯IEEE754计算中复制这个(舍入)过程?
答:
获得两个“接近”IEEE754双精度值之间的“精确”差异?
使用有限浮点运算通常无法做到这一点,因为精确的差值不一定可以用浮点类型表示。
这可以通过将浮点数转换为任意精度表示,并使用该任意精度计算结果来实现。标准库中没有任意精度类型。
评论
x
y
评论
655.36
655.34
get_delta
655.36000000000001...
655.34000000000003...
655.36
655.34
double
printf"%a %a %a", lhs, rhs, result);
Specifically:
f the naive delta calculation could be improved
long double
IFF we were to calculate using an infinite precision type
1.9
|a| > |b|
10e-15*fabs(a)
DBL_EPSILON(fabs(a))