在C++中检查double是否为2的幂而没有位操作的代码

Gam*_*per 6 c++ floating-point double

为了检查double是否是2的幂,我发现了这段代码:

unsigned long long int &p= *(unsigned long long int *) &x;
unsigned int exp= (p >> 52) & 0x7FF;
if ( exp == 0 || exp == 0x7FF ) return false;
return (p & 0xFFFFFFFFFFFFFULL) == 0; 
Run Code Online (Sandbox Code Playgroud)

但是它无法对某些体系结构进行基本测试.我想这是因为整数的长度不同.所以我试图找出一个不做位操作的简单替代方案:

bool isPot(double a ){
    return a==0.? false : (1./a)*a==1.;
}
Run Code Online (Sandbox Code Playgroud)

假设任何除以2的幂的除数在尾数中产生无限数字,因此由于值被截断,所以当乘以其倒数时它不会产生1.

然而,它显然是有效的,但我无法证明,因为对所有值进行测试需要大约140年.

建议?

MyTests:

assert(isPot(2.0)); //first solution fails here
assert(isPot(0.5));
assert(!isPot(5.0));
assert(!isPot(0.2));
Run Code Online (Sandbox Code Playgroud)

通过2的幂,我的意思是一旦存储在RAM中,该值就是2的幂.所有尾数位为0的数字.这隐含地是一个具有固有错误的解决方案,因为假设以下值:

2.000000000000000000000000000000000000000000000000000000000000003
Run Code Online (Sandbox Code Playgroud)

它将被转换为

2.0
Run Code Online (Sandbox Code Playgroud)

所以返回true是因为所有尾数位都为0,但最初它不是2的幂.

Ala*_*kes 15

您可以使用frexp作为便携方式将double分割为指数和尾数,然后检查尾数是否正好为0.5.

例:

#include <math.h>

bool isPow2(double x)
{
    int exponent = 0;
    auto mantissa1 = frexp(x, &exponent);
    return mantissa1 == 0.5;
}

BOOST_AUTO_TEST_CASE(powers_of_2)
{
    std::vector<double> yes { 1, 2, 4, 8, 16, 0.5, 0.25, 65536 };
    std::vector<double> no { 0, 3, 7, 15, 0.51, 0.24, 65535 };


    for (size_t i = 0 ; i < yes.size() ; ++i) {
        BOOST_CHECK_EQUAL(isPow2(yes[i]), true);
    }
    for (size_t i = 0 ; i < no.size() ; ++i) {
        BOOST_CHECK_EQUAL(isPow2(no[i]), false);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @DarioOO:你为什么不想硬编码`0.5`?`frexp`是标准化的,因此是便携式的. (3认同)