Floating point 平方根逼近误差界的推导

Floating point 平方根逼近误差界的推导,floating-point,floating-accuracy,Floating Point,Floating Accuracy,关于四元数的标准化问题,作者提供了一些计算平方根逆的代码,使用2.0/(1.0+qmagsq)作为1.0/std::sqrt(qmagsq)的近似值,计算非常接近1的值: double qmagsq = quat.square_magnitude(); if (std::abs(1.0 - qmagsq) < 2.107342e-08) { quat.scale (2.0 / (1.0 + qmagsq)); } else { quat.scale (1.0 / std::

关于四元数的标准化问题,作者提供了一些计算平方根逆的代码,使用
2.0/(1.0+qmagsq)
作为
1.0/std::sqrt(qmagsq)
的近似值,计算非常接近1的值:

double qmagsq = quat.square_magnitude();
if (std::abs(1.0 - qmagsq) < 2.107342e-08) {
    quat.scale (2.0 / (1.0 + qmagsq));
} else {
    quat.scale (1.0 / std::sqrt(qmagsq));
}
double qmagsq=quat.square_震级();
if(标准::abs(1.0-qmagsq)<2.107342e-08){
四分量表(2.0/(1.0+qmagsq));
}否则{
四分量表(1.0/std::sqrt(qmagsq));
}
然后,作者提供了以下解释:

对于介于0和2之间的
qmagsq
,此近似值的误差小于
(1-qmagsq)^2/8
。幻数2.107342e-08表示此错误超过IEEE Double的ULP的一半

这大概是因为
sqrt(8*2^-(1+52)/2)
约为2.10734243e-8,其中
2^-(1+52)/2
双精度的一半

如何推导出
(1-qmagsq)^2/8
作为
qmagsq
值在0和2之间的近似值的误差上限

编辑:

已经指出,作者为
qmagsq
值提供的误差范围介于0和1之间。因此,问题变得更加开放:


如何推导该近似值的误差界,以确定该近似值的误差小于IEEE Double的半个ULP的范围?

计算机已经足够快,可以在整个输入域内对单个参数函数的特定断言进行彻底的测试单精度,以及双精度的合理小间隔。实际界限通常约为248个测试向量左右。我假设使用的是IEEE-754兼容平台,默认舍入模式舍入为最接近或甚至,并且所有代码都是按照编译器可以集合的IEEE-754的最严格要求构建的(例如,对于我的英特尔编译器,这是
/fp:strict

问题中的说法是,快速更换在unity附近实现了0.5 ulp或更小的误差。换句话说,使用IEEE-754舍入模式将结果正确舍入为最接近或偶数。测试该断言有两种方法:使用正确舍入的
rsqrt()
实现作为参考,并在1个ulp步骤中迭代参数,直到发现不匹配,或者使用多精度库作为参考,并在快速替代方案的ulp错误超过0.5 ulp时停止。在后一种情况下,我们需要的参考结果的精度是双精度的两倍多一点,以避免双舍入效应。对于倒数平方根,具有2n+3位的参考就足够了:

Cristina Iordache和David W.Matula:“关于除法、平方根、倒数和平方根倒数的无限精确舍入”。1999年4月14日至16日,澳大利亚阿德莱德,第14届IEEE计算机算术研讨会论文集,第233-240页

下面的ISO-C99代码使用第一种方法。它在单位处开始搜索,然后在零或二的方向上搜索,在第一个不匹配处停止。结果如下:

arg = (1.0 + 2.2204460492503131e-016)  quick_rsqrt =  0x1.0000000000000p+0 (1.0000000000000000e+000)  rsqrt_rn =  0x1.fffffffffffffp-1 (9.9999999999999989e-001)   
arg = (1.0 - 1.2166747276332046e-008)  quick_rsqrt =  0x1.0000001a20bd7p+0 (1.0000000060833736e+000)  rsqrt_rn =  0x1.0000001a20bd8p+0 (1.0000000060833738e+000)
我还尝试了第二种方法,得到了匹配的结果。(1.0+2.2204460492503131e-16)处的快速更换误差为0.9995 ulps,(1.0-1.216674726332046E-8)处的误差为0.5002 ulps

#包括
#包括
#包括
#包括
#包括
/*正在测试的功能*/
双快速(双a)
{
回报率2.0/(1.0+a);
}
/*倒数平方根的起始近似*/
双简支(双a)
{
返回1.0/平方米(a);
}
/*IEEE-754二进制文件的最高有效位32位表示64*/
双精度(双a)中的uint32\t高uint32\u
{
uint64_t;
memcpy(t&a,t&a,sizeof t);
返回(uint32_t)(t>>32);
}
/*IEEE-754二进制文件的最低有效位32位表示64*/
双精度(双a)中的uint32\t低uint32\u
{
uint64_t;
memcpy(t&a,t&a,sizeof t);
返回(uint32_t)t;
}
/*从其位表示的上半部分和下半部分构造IEEE-754二进制64*/
双mk双UU来自Uhilo双uint32(uint32高,uint32低)
{
双r;
uint64_t=((uint64_t)hi>1;//调整指数;确保ISO-C99不保证的算术右移
a=从双精度(y)中的高精度+双精度(y)中的低精度;
}如果(a==0.0){//0,则为else
a=mk_double_,从_hilo_uint32开始((ahi&0x8000000)| 0x7ff00000,0x00000000);
}如果(a<0.0){//否定,则为else
a=从uHILO uUINT32(0xfff80000,0x00000000)开始的mk\u double\u;
}else如果(isinf(a)){//无穷大
a=来自hilo uint32的mk\u double\u(ahi&0x80000000,0x00000000);
}else if(isnan(a)){//NaNs
a=a+a;
}else{//正次正态
/*走向统一的尺度论证*/
a=a*mk\u-double\u来自于uhilo\u uint32(0x7fd00000,0);
/*初始rsqrt近似*/
y=简单的_rsqrt(a);
/*改进倒数平方根近似*/
h=y*y;
l=fma(y,y,-h);
e=fma(l,-a,fma(h,-a,1.0));
/*根据Peter Markstein的“IA-64和初等函数”进行循环*/
y=fma(fma(0.375,e,0.5),e*y,y);
/*将结果缩放到接近单位的正确范围*/
a=mk_double_从_hilo_uint32(y的hi_uint32_)+0x1ff00000,y的lou uint32_);
}
返回a;
}
内部主(空)
{
双x,ref,res;
/*尝试比团结更大的争论*/
x=1.0;
做{
res=快速_rsqrt(x);
ref=rsqrt_rn(x);
如果(res!=ref){
printf(“arg=(1.0+%23.16e)quick\u rsqrt=%21.13a(%23.16e)rsqrt\u rn=%21.13a(%23.16e)\n”,