a + sqrt(b)
的两个值,其中a
和b
是无符号整数。因为这是紧密循环的一部分,所以我希望这个比较尽可能快地运行。(如果有影响的话,我在x86-64机器上运行代码,并且无符号整数不大于10^6。另外,我知道a1<a2
的事实。)这是我试图优化的独立函数。我的数字是足够小的整数,以至于
double
(甚至float
)可以精确地表示它们,但sqrt
的舍入误差结果必须不改变结果。// known pre-condition: a1 < a2 in case that helps
bool is_smaller(unsigned a1, unsigned b1, unsigned a2, unsigned b2) {
return a1+sqrt(b1) < a2+sqrt(b2); // computed mathematically exactly
}
测试用例: is_smaller(900000, 1000000, 900001, 998002)
应该返回true,但是根据@wim的评论显示,使用sqrtf()
将返回false。使用(int)sqrt()
截断为整数也会如此。
a1 + sqrt(b1) =90100
和a2+sqrt(b2)=901000.00050050037512481206
。最接近这个结果的浮点数是精确等于90100。
由于即使在完全内联作为sqrtsd
指令时,在现代x86-64上sqrt()
函数通常也非常昂贵,因此我尽可能地避免调用sqrt()
。
通过平方来消除平方根,还有可能避免任何四舍五入误差,从而使所有计算都变得准确无误。
如果函数像这样...
bool is_smaller(unsigned a1, unsigned b1, unsigned x) {
return a1+sqrt(b1) < x;
}
...那么我可以这样做:return x-a1>=0 && static_cast<uint64_t>(x-a1)*(x-a1)>b1;
但是现在由于有两个sqrt(...)
项,我无法进行相同的代数运算。
我可以使用这个公式将值平方两次:
a1 + sqrt(b1) = a2 + sqrt(b2)
<==> a1 - a2 = sqrt(b2) - sqrt(b1)
<==> (a1 - a2) * (a1 - a2) = b1 + b2 - 2 * sqrt(b1) * sqrt(b2)
<==> (a1 - a2) * (a1 - a2) = b1 + b2 - 2 * sqrt(b1 * b2)
<==> (a1 - a2) * (a1 - a2) - (b1 + b2) = - 2 * sqrt(b1 * b2)
<==> ((b1 + b2) - (a1 - a2) * (a1 - a2)) / 2 = sqrt(b1 * b2)
<==> ((b1 + b2) - (a1 - a2) * (a1 - a2)) * ((b1 + b2) - (a1 - a2) * (a1 - a2)) / 4 = b1 * b2
由于无符号除以4只是一个位移操作,所以它很便宜,但是由于我要将数字平方两次,所以我需要使用128位整数,并且我需要引入一些>=0
检查(因为我比较的是不等式而不是等式)。
感觉可能有更好的代数方法来更快地解决这个问题。是否有更快的方法?
a1+sqrt(b1)<a2
成立,那么可以跳过计算sqrt(b2)
。 - 500 - Internal Server Errora1 < a2
,那么你可以直接排除所有满足b1 < b2
条件的情况。 - kvantour