获得两个“接近”IEEE754双精度值之间的“精确”差异?

Get "precise" difference between two "near" IEEE754 double values?

提问人:Martin Ba 提问时间:2/9/2022 最后编辑:Martin Ba 更新时间:2/12/2022 访问量:221

问:

我什至不确定我所要求的是否可能,但这里是:

我们的 C++ 代码执行以下计算:

double get_delta(double lhs, double rhs) {
  return lhs - rhs;
}

并且输入(最接近的 double to),这不会产生“最接近”的 double 到 ,而是产生一个更接近 的值。655.36655.340.020.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.02v3v4

那么,有没有办法对起始值进行归一化,以便在纯IEEE754计算中复制这个(舍入)过程?

C++ 浮点 精度 IEEE-754

评论

3赞 Ted Lyngmo 2/9/2022
我不确定我是否理解,但输入不是和在.他们是,所以,已经太晚了。既不能也不能完全用 IEEE-754 表示。655.36655.34get_delta655.36000000000001...655.34000000000003...655.36655.34double
1赞 Öö Tiib 2/9/2022
IEEE754双精度具有二元有效,因此不可能精确地表示 0.02。如果您需要最多 15 位十进制数字的固定点值,那么int64_t似乎足够好,因为它有 18 位十进制数字。
0赞 KamilCuk 2/9/2022
请发布您的输入数字和输出数字的确切值 - 来自输入和输出数字。 您是否发布了一些 javascript 浏览器代码的输出? 是的。更精确! 还是更精确?en.wikipedia.org/wiki/...... 你怎么知道输入不是这样四舍五入的,所以结果是?printf"%a %a %a", lhs, rhs, result);Specifically:f the naive delta calculation could be improvedlong doubleIFF we were to calculate using an infinite precision type1.9
0赞 Robert Andrzejuk 2/9/2022
也许这可以提供更多的启示:learn.microsoft.com/en-us/cpp/build/reference/......
1赞 chux - Reinstate Monica 2/10/2022
“你如何计算结果差值的精度?” --> 如果 ,差值的精度约为 或 ,可能更好。|a| > |b|10e-15*fabs(a)DBL_EPSILON(fabs(a))

答:

-2赞 eerorika 2/9/2022 #1

获得两个“接近”IEEE754双精度值之间的“精确”差异?

使用有限浮点运算通常无法做到这一点,因为精确的差值不一定可以用浮点类型表示。

这可以通过将浮点数转换为任意精度表示,并使用该任意精度计算结果来实现。标准库中没有任意精度类型。

评论

0赞 Robert Harvey 2/9/2022
OP在他们的问题中已经有了这些澄清。
0赞 eerorika 2/10/2022
@RobertHarvey我知道。OP回答了他们自己的问题。这并不能改变答案。
2赞 Mark Dickinson 2/10/2022
另请参阅 Sterbenz 引理,这意味着对于两个“接近”的 IEEE 754 binary64 值和(“接近”是指它们具有相同的符号并且在大小上相差 2 倍以内),差异必然可以用 binary64 格式精确表示。因此,对于从这个意义上说接近的两个值,在执行减法之前将值转换为更高的精度将无济于事。xy