C 四舍五入双精度整数,以避免在后续求和时四舍五入

C 四舍五入双精度整数,以避免在后续求和时四舍五入,c,floating-point,double,ieee-754,C,Floating Point,Double,Ieee 754,我如何实现这一点 // round to the nearest double so that x + ref doesn't cause round off error double round(double x, double ref) { } 所以 double x = ....; double y = ....; double x_new = round(x, y); return x_new + y; // NO ROUND OFF! 换句话说(y+x_new)-x_new严格

我如何实现这一点

// round to the nearest double so that x + ref doesn't cause round off error
double round(double x, double ref) { }
所以

double x = ....;
double y = ....;

double x_new = round(x, y);
return x_new + y; // NO ROUND OFF! 

换句话说(y+x_new)-x_new严格等同于y

在Python中可以直接转换为C的可能解决方案

import math
from decimal import Decimal


def round2(x, ref):
    x_n, x_exp     = math.frexp(x)
    ref_n, ref_exp = math.frexp(ref)
    assert x_exp <= ref_exp
    diff_exp = ref_exp - x_exp

    factor = 2. ** (53 - diff_exp)

    x_new_as_int = int(round(x_n * factor))
    x_new_as_norm_float = float(x_new_as_int) / factor
    return math.ldexp(x_new_as_norm_float, x_exp)


x = 0.001
y = 1.0

assert (y + x) - x != y

x_new = round2(x, y)

assert (y + x_new) - x_new == y

print "x:", Decimal(x)
print "x_new:", Decimal(x_new)

print "relative difference:", (x_new/x - 1.) 
导入数学
从十进制输入十进制
def第2轮(x,参考):
x_n,x_exp=math.frexp(x)
ref\u n,ref\u exp=math.frexp(ref)

断言x_exp让我们假设
x
y
都是正的

S
为双精度和
x+y

有两种情况:

  • 如果
    x
    y
    ,那么,
    S-y
    由Sterbenz引理精确。因此,加法
    (S-y)+y
    是精确的(它精确地生成
    S
    ,这是一个双精度数字)。因此,您可以为
    x\u new
    选择
    S-y
    。不仅
    y+x\u new
    是精确的,而且它产生与
    y+x
    相同的结果
    S

  • 如果
    x
    y
    ,则根据
    y
    有效位中设置的位数,您可能会遇到问题。例如,如果设置了
    y
    的有效位中的最后一位,则
    y
    的二进制位之后的二进制位中的任何数字
    z
    都不能具有
    z+y
    精确的属性



这个答案与你的想法有着模糊的联系。

这通常不可能以你想要的方式实现。文本“到最近的双精度”表示您希望返回
x
附近的
x\u new
。但是,如果
x
大于
y
,则在
x
附近的某个数字的
y
的任何相加都将迫使
y
的一些低位超出总和。例如,假设
x
为264,
y
为1。满足要求的最接近的
双精度
(使用IEEE-754 64位二进制)是253-1,这根本不接近2**64。@eric,这是正确的。假设x归一化指数(由frexp返回)小于或等于y/ref归一化指数,因为“双舍入”已经有了一个特定的含义,“双”用作形容词。请注意检查
(y+x_new)-x_new==y
与检查
x_new+y
是否准确不同。后者意味着前者,但不是必需的。示例:
y=1e300,x\u new=1
(y+x_new)-x_new==1e300==y
但是添加的
y+x_new
不精确。看起来此解决方案依赖于IEEE-754 64位二进制文件。C不将
double
指定为。您是否正在寻找一种通用于C的解决方案?@chux它肯定应该是C99可移植的。我不知道ieee-754 64位c99 double.0B100000110001001101110100101111000110101001111100@paolo_losi我太慢了,得出的结论是我的解决方案有点不对劲(很好,你找到了一个例子)。确切地说,我想知道将我的解决方案更改为
2*y-(2*y-x)
是否会使它正确(仍然假设为0)≤ <代码>x
≤ <代码>y
)。至少现在我可以试一下你的例子,如果还不能证明的话,这将是一个线索。@paolo_losi我已经修改了我的答案很多。它现在包含了一个与我先前想要的答案(0)相同的假设的明显正确的解决方案≤ <代码>x
≤ <代码>y)。在
x
大于
y
的情况下,可能无法提供令人满意的
x\u new
,例如如果
y
的有效位以设定位结束。