你已经给你的问题一个很好的和优雅的解决方案:
如果计算有理数很便宜,这将是简单的:值 对于x将包含在合理的双精度数(b-a-0.5 * ulp1(b)... b-a + 0.5 * ulp2(b))。如果b是偶数,则应该包括边界 ,如果b是奇数,则应该包括边界 ,并且ulp1和 ulp2是两个稍微不同的“ULP”的定义,如果不介意 的功率失去一点精度二。
以下是基于本段落的问题的部分解决方案的半理由草图。希望我有机会尽快充实它。要得到真正的解决方案,你必须处理低于正常值,零点,NaN和所有其他有趣的东西。我假设a
和b
是,例如,1e-300 < |a| < 1e300
和1e-300 < |b| < 1e300
,以便在任何点都不会发生疯狂。
没有上溢和下溢,您可以从b - nextafter(b, -1.0/0.0)
得到ulp1(b)
。你可以从nextafter(b, 1.0/0.0) - b
得到ulp2(b)
。
如果b/2 <= a <= 2b
,那么Sterbenz定理告诉你b - a
是确切的。所以(b - a) - ulp1/2
将是最接近的double
到下限和(b - a) + ulp2/2
将是最接近的double
到上限。尝试使用这些值,以及之前和之后的值,并选择最宽的工作时间间隔。
如果b > 2a
,b - a > b/2
。计算出的值b - a
最多不超过半个ulp。一个ulp1
是最多两个ulp,因为是一个ulp2
,所以您给出的合理间隔最多为两个ulp。找出与b-a
工作的五个最接近的值中的哪一个。
如果是a > 2b
,那么b-a
的ulp至少与ulpb
的ulp一样大;如果有的话,我敢打赌它必须是b-a
的三个最接近的值之一。我想象a
和b
具有不同的标志作品的情况相似。
我写了一小堆实现这个想法的C++代码。在无聊等待之前,它没有失败随机模糊测试(在几个不同的范围内)。这里是:
void addeq_range(double a, double b, double &xlo, double &xhi) {
if (a != a) return; // empty interval
if (b != b) {
if (a-a != 0) { xlo = xhi = -a; return; }
else return; // empty interval
}
if (b-b != 0) {
// TODO: handle me.
}
// b is now guaranteed to be finite.
if (a-a != 0) return; // empty interval
if (b < 0) {
addeq_range(-a, -b, xlo, xhi);
xlo = -xlo;
xhi = -xhi;
return;
}
// b is now guaranteed to be zero or positive finite and a is finite.
if (a >= b/2 && a <= 2*b) {
double upulp = nextafter(b, 1.0/0.0) - b;
double downulp = b - nextafter(b, -1.0/0.0);
xlo = (b-a) - downulp/2;
xhi = (b-a) + upulp/2;
if (xlo + a == b) {
xlo = nextafter(xlo, -1.0/0.0);
if (xlo + a != b) xlo = nextafter(xlo, 1.0/0.0);
} else xlo = nextafter(xlo, 1.0/0.0);
if (xhi + a == b) {
xhi = nextafter(xhi, 1.0/0.0);
if (xhi + a != b) xhi = nextafter(xhi, -1.0/0.0);
} else xhi = nextafter(xhi, -1.0/0.0);
} else {
double xmid = b-a;
if (xmid + a < b) {
xhi = xlo = nextafter(xmid, 1.0/0.0);
if (xhi + a != b) xhi = xmid;
} else if (xmid + a == b) {
xlo = nextafter(xmid, -1.0/0.0);
xhi = nextafter(xmid, 1.0/0.0);
if (xlo + a != b) xlo = xmid;
if (xhi + a != b) xhi = xmid;
} else {
xlo = xhi = nextafter(xmid, -1.0/0.0);
if (xlo + a != b) xlo = xmid;
}
}
}
你可以使用二分法搜索平分所需的值吗?看起来这应该是可能的,因为位数很低。 – templatetypedef
@templatetypedef我画出的“128位浮点加法”解决方案是对浮点数的表示进行二分查找,而我不想显示的是因为我不知道它是否实际上是一个浮点数改进通过计算候选的过度逼近范围将初始区间缩小为二等分,然后需要通过二分搜索来改进。 –
@templatetypedef我有点希望有人会提出一个浮点运算的定理,它可以更加优雅地解决问题。 –