使用c ++中的按位运算来隔离double的小数部分

时间:2015-01-26 23:35:37

标签: c++

我被困在我现代数值软件开发课的任务上。

函数原型(假设x = 6.5):

//returns the IEEE fractional part of x as a decimal floating point number. You must convert binary to decimal.
inline double fraction(double x) {}

我得到了什么:

inline double fraction(double x)
{
    // Get the fraction
    unsigned long long frac_mask = (1u << 52) - 1;                      // Get 52 1's
    unsigned long long xint = *reinterpret_cast<long long*>(&x);        // Interpret x's bits as an int
    unsigned long long frac_num = xint & frac_mask;                                 // Get the fraction as an int
    double fraction = double(frac_num) / double(2u << 52);              // Divide frac_num by 2^52

    return fraction;

    /* This code works, but is not what is specified:
        double fraction = x / pow(2, exponent(x));
        fraction = fmod(fraction, 1);
        return fraction;
    */
}

我一直在拿NaN。我要找的答案是0.625。我有点绝望地迷失了。非常感谢任何帮助。

我能够使用以下函数成功地隔离double的指数:

inline int exponent(double x) //returns the unbiased(true) binary exponent of x as a decimal integer. Remember that subnormals are a special case. Consider 0 to be a subnormal.
{
    if (x == 0.0)
        return -1022;
    else if (isnan(x))
        return 1024;

    // Get the exponent
    unsigned long long exp_mask = (1u << 11) - 1;                       // Get eleven 1's
    exp_mask <<= 52;                                                    // Move into place
    unsigned long long xint = *reinterpret_cast<long long*>(&x);        // Interpret x's bits as an int
    unsigned long long exp_bits = xint & exp_mask;                      // Get the exponent bits
    unsigned long long exp = exp_bits >> 52;                            // Get the exponent as a number
    return exp -1023;
}

我很困惑为什么指数逻辑工作,但分数不会。

1 个答案:

答案 0 :(得分:2)

您正在将unsigned(可能是32位)与需要64位的值混合。

例如,frac_num只有32位,使用longlong long ... [或uint64_t,这是一种更可靠的获取方式64位值。

inline double fraction(double x)
{
    // Get the fraction
    uint64_t frac_mask = (1ul << 52) - 1;                      // Get 52 1's
//    uint64_t xint = *reinterpret_cast<uint64_t*>(&x);        // Interpret x's bits as an int
      uint64_t xint; 
      memcpy(&xint, &x, sizeof(xint));        // Interpret x's bits as an int
    int64_t frac_num = xint & frac_mask;                                    // Get the fraction as an int
    frac_num += 1ul << 52; // Add hidden bit.
    double fraction = double(frac_num) / double(2ul << 52);              // Divide frac_num by 2^52

    return fraction;
}

请注意向l1u添加2u,以确保它们为long,以及。您需要包含cstdint才能获得大小整数。

编辑:那当然只是给你一个分数形式的尾数。小数点可以是位1023和-1023之间的任何位置,这意味着只有-1和+1之间的值才能得到正确的结果。

使用上面代码[+ some printouts]

的完整示例
#include <cstdint>
#include <iostream>
#include <cstring>

inline double fraction(double x)
{
    // Get the fraction
    uint64_t frac_mask = (1ul << 52) - 1;                      // Get 52 1's
    std::cout << "mask=" << std::hex << frac_mask << std::endl;
//    uint64_t xint = *reinterpret_cast<uint64_t*>(&x);        // Interpret x's bits as an int
      uint64_t xint; 
      memcpy(&xint, &x, sizeof(xint));        // Interpret x's bits as an int
    int64_t frac_num = xint & frac_mask;                                    // Get the fraction as an int

    frac_num += 1ul << 52; // Add hidden bit.
    std::cout << "xint=" << std::hex << xint << " num=" << std::hex << frac_num << std::endl;
    double fraction = double(frac_num) / double(2ul << 52);              // Divide frac_num by 2^52

    return fraction;
}


int main()
{
    double a = 0.5;
    double b = 0.75;
    double d = 6.5;
    double e = 4.711;


    double fa  = fraction(a);
    double fb  = fraction(b);
    double fd  = fraction(d);
    double fe  = fraction(e);

    std::cout << "fa=" << std::fixed << fa << " fb=" << fb << std::endl;
    std::cout << "fd=" << std::fixed << fd << " fe=" << fe << std::endl;
}

运行上述内容:

mask=fffffffffffff
xint=3fe0000000000000 num=10000000000000
mask=fffffffffffff
xint=3fe8000000000000 num=18000000000000
mask=fffffffffffff
xint=401a000000000000 num=1a000000000000
mask=fffffffffffff
xint=4012d810624dd2f2 num=12d810624dd2f2
fa=0.500000 fb=0.750000
fd=0.812500 fe=0.588875

请注意,如果将4.711除以2几次[确切地说是3次],则得到0.588875,如果将6.5除以8(或除以3得3),则得到0.8125

我需要去睡觉,但你基本上必须考虑指数来计算浮点数的分数。或者简单地转换为整数,然后减去它 - 只要它在范围内。