@tmyklebu 完美地回答了这个问题。作为补充,让我们看看在没有 asm 指令的情况下测试分数的完全平方的效率可能较低的替代方法。
假设我们有一个符合 IEEE 754 的 sqrt,它可以正确地对结果进行四舍五入。
假设已经处理了异常值 (Inf/Nan) 和零 (+/-)。
让我们将sqrt(x) 分解为I*2^m,其中I 是一个奇数。
其中I 跨越 n 位:1+2^(n-1) <= I < 2^n。
如果 n > 1+floor(p/2) 其中 p 是浮点精度(例如 p=53 和 n>27 在双精度)
然后2^(2n-2) < I^2 < 2^2n.
由于I 是奇数,I^2 也是奇数,因此跨越 > p 位。
因此I 不是任何具有这种精度的可表示浮点的精确平方根。
但是给定I^2<2^p,我们可以说x 是一个完美的正方形吗?
答案显然是否定的。泰勒展开式会给出
sqrt(I^2+e)=I*(1+e/2I - e^2/4I^2 + O(e^3/I^3))
因此,对于e=ulp(I^2) 到sqrt(ulp(I^2)),平方根正确舍入为rsqrt(I^2+e)=I...(舍入到最接近的偶数或截断或取整模式)。
因此我们必须断言sqrt(x)*sqrt(x) == x。
但上述试验是不充分的,例如,假设IEEE 754双精度,sqrt(1.0e200)*sqrt(1.0e200)=1.0e200,其中1.0e200正是99999999999999996973312221251036165947450327545502362648241750950346848435554075534196338404706251868027512415973882408182135734368278484639385041047239877871023591066789981811181813306167128854888448其第一素因数是2^613,任何分数... P>的几乎完美的方
所以我们可以结合两个测试:
#include <float.h>
bool is_perfect_square(double x) {
return sqrt(x)*sqrt(x) == x
&& squared_significand_fits_in_precision(sqrt(x));
}
bool squared_significand_fits_in_precision(double x) {
double scaled=scalb( x , DBL_MANT_DIG/2-ilogb(x));
return scaled == floor(scaled)
&& (scalb(scaled,-1)==floor(scalb(scaled,-1)) /* scaled is even */
|| scaled < scalb( sqrt((double) FLT_RADIX) , DBL_MANT_DIG/2 + 1));
}
编辑:
如果我们想限制为整数的情况,我们还可以检查floor(sqrt(x))==sqrt(x) 或在 squared_significand_fits_in_precision 中使用脏位黑客...