从 double 中窃取 3 个最低有效位?

Steal the 3 least significant bits from a double?

我正在研究一种编程语言,简而言之,我需要从双精度中窃取 3 个最低有效位,当使用整数时,我可以执行以下操作

long long int make(long long int x)
{
    return x << 3;
}
long long int take(long long int x)
{
    return x >> 3;
}

然而,当这样做加倍时(当然首先转换成一个 long long 二进制表示)它只会使数字不像以前那样。 值得一提的是,我的代码只会发送到 CHAR_BIT == 8.

的平台

想法?

typedef union
{
    double d;
    struct
    {
        uint64_t frac   :52;
        uint64_t exp    :11;
        uint64_t sign   :1;
    };
}DBL;

double steal(double val, int nbits)
{
    uint64_t mask = ~((1ULL << nbits) - 1);
    DBL d = {.d = val};
    d.frac &= mask;
    return d.d;
}


int main(void)
{
    double x = 1.0/6;
    printf("%.100f\n", x);
    printf("%.100f\n", steal(x, 3));
    x = 3.14658456758768765878445676547564765765657567567657754836459845798457498457;
    printf("%.100f\n", x);
    printf("%.100f\n", steal(x, 20));
}

https://godbolt.org/z/dh1Ge7YYe

也许是这样的。它不可移植,因为位域是实现定义的,但是 gcc、clang、IAR、Keil、GHC 以这种方式实现它们。

由于 C++ 中不允许类型双关,这里有一个版本将 double 复制到 unsigned char 数组并清除其中的 3 个最低有效位,然后将其复制回双精度数:

#include <bit>
#include <cstring>
#include <limits>

double take(double x) {
    static_assert(std::endian::native == std::endian::little ||
                  std::endian::native == std::endian::big);
    static_assert(std::numeric_limits<double>::is_iec559);
    unsigned char buf[sizeof x];
    std::memcpy(buf, &x, sizeof x);
    if constexpr(std::endian::native == std::endian::little)
        buf[0] &= 0b11111000;
    else
        buf[sizeof buf - 1] &= 0b11111000;
    std::memcpy(&x, buf, sizeof x);
    return x;
}

Demo