从 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;
}
我正在研究一种编程语言,简而言之,我需要从双精度中窃取 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;
}