将 float 转换为 bigint (又名获取二进制指数和尾数的便携式方法)

发布于 2024-08-19 11:04:46 字数 1348 浏览 2 评论 0原文

在 C++ 中,我有一个 bigint 类,可以保存任意大小的整数。

我想将大浮点数或双精度数转换为 bigint。 我有一个工作方法,但是有点麻烦。我使用 IEEE 754 数字规范来获取输入数字的二进制符号、尾数和指数。

这是代码(这里忽略了 Sign,这并不重要):

 float input = 77e12;
 bigint result;

 // extract sign, exponent and mantissa, 
 // according to IEEE 754 single precision number format
 unsigned int *raw = reinterpret_cast<unsigned int *>(&input); 
 unsigned int sign = *raw >> 31;
 unsigned int exponent = (*raw >> 23) & 0xFF;
 unsigned int mantissa = *raw & 0x7FFFFF;

 // the 24th bit is always 1.
 result = mantissa + 0x800000;

 // use the binary exponent to shift the result left or right
 int shift = (23 - exponent + 127);
 if (shift > 0) result >>= shift; else result <<= -shift;

 cout << input << " " << result << endl;

它可以工作,但它相当难看,而且我不知道它的可移植性如何。有更好的方法吗?有没有一种不太丑陋、可移植的方法来从浮点数或双精度数中提取二进制尾数和指数?


感谢您的回答。对于后代,这里有一个使用 frexp 的解决方案。由于循环的原因,它的效率较低,但它适用于浮点型和双精度型,不使用reinterpret_cast或依赖于浮点数表示的任何知识。

float input = 77e12;
bigint result;

int exponent;
double fraction = frexp (input, &exponent);
result = 0;
exponent--;
for (; exponent > 0; --exponent)
{
    fraction *= 2;
    if (fraction >= 1)
    {
        result += 1;
        fraction -= 1;
    }
    result <<= 1;
}   

In C++, I have a bigint class that can hold an integer of arbitrary size.

I'd like to convert large float or double numbers to bigint.
I have a working method, but it's a bit of a hack. I used IEEE 754 number specification to get the binary sign, mantissa and exponent of the input number.

Here is the code (Sign is ignored here, that's not important):

 float input = 77e12;
 bigint result;

 // extract sign, exponent and mantissa, 
 // according to IEEE 754 single precision number format
 unsigned int *raw = reinterpret_cast<unsigned int *>(&input); 
 unsigned int sign = *raw >> 31;
 unsigned int exponent = (*raw >> 23) & 0xFF;
 unsigned int mantissa = *raw & 0x7FFFFF;

 // the 24th bit is always 1.
 result = mantissa + 0x800000;

 // use the binary exponent to shift the result left or right
 int shift = (23 - exponent + 127);
 if (shift > 0) result >>= shift; else result <<= -shift;

 cout << input << " " << result << endl;

It works, but it's rather ugly, and I don't know how portable it is. Is there a better way to do this? Is there a less ugly, portable way to extract the binary mantissa and exponent from a float or double?


Thanks for the answers. For posterity, here is a solution using frexp. It's less efficient because of the loop, but it works for float and double alike, doesn't use reinterpret_cast or depend on any knowledge of floating point number representations.

float input = 77e12;
bigint result;

int exponent;
double fraction = frexp (input, &exponent);
result = 0;
exponent--;
for (; exponent > 0; --exponent)
{
    fraction *= 2;
    if (fraction >= 1)
    {
        result += 1;
        fraction -= 1;
    }
    result <<= 1;
}   

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

过潦 2024-08-26 11:04:46

Can't you normally extract the values using frexp(), frexpf(), frexpl()?

独自←快乐 2024-08-26 11:04:46

我喜欢你的解决方案!它让我走上了正轨。

不过,我建议一件事 - 为什么不一次获得一堆位并且几乎总是消除任何循环?我实现了一个像这样的 float-to-bigint 函数:(

template<typename F>
explicit inline bigint(F f, typename std::enable_if<(std::is_floating_point<F>::value)>::type* enable = nullptr) {
    int exp;
    F fraction = frexp(fabs(f),&exp);
    F chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value);
    *this = ulong(chunk); // will never overflow; frexp() is guaranteed < 1
    exp -= ulong_bit_count;
    while (sizeof(F) > sizeof(ulong) && (fraction -= chunk)) // this is very unlikely
    {
        chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value);
        *this <<= ulong_bit_count;
        (*this).data[0] = ulong(chunk);
        exp -= ulong_bit_count;
    }
    *this <<= exp;
    sign = f < 0;
}

顺便说一句,我不知道有一种简单的方法来放入浮点二次方常量,所以我定义了 float_pow_2 如下):

template<typename F, unsigned Exp, bool Overflow = (Exp >= sizeof(unsigned))>
struct float_pow_2 {
    static constexpr F value = 1u << Exp;
};
template<typename F, unsigned Exp>
struct float_pow_2<F,Exp,true> {
    static constexpr F half = float_pow_2<F,Exp/2>::value;
    static constexpr F value = half * half * (Exp & 1 ? 2 : 1);
};

I like your solution! It got me on the right track.

I'd recommend one thing though - why not get a bunch of bits all at once and almost always eliminate any looping? I implemented a float-to-bigint function like this:

template<typename F>
explicit inline bigint(F f, typename std::enable_if<(std::is_floating_point<F>::value)>::type* enable = nullptr) {
    int exp;
    F fraction = frexp(fabs(f),&exp);
    F chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value);
    *this = ulong(chunk); // will never overflow; frexp() is guaranteed < 1
    exp -= ulong_bit_count;
    while (sizeof(F) > sizeof(ulong) && (fraction -= chunk)) // this is very unlikely
    {
        chunk = floor(fraction *= float_pow_2<F,ulong_bit_count>::value);
        *this <<= ulong_bit_count;
        (*this).data[0] = ulong(chunk);
        exp -= ulong_bit_count;
    }
    *this <<= exp;
    sign = f < 0;
}

(By the way, I don't know of an easy way to put in floating point power-of-two constants, so I defined float_pow_2 as follows):

template<typename F, unsigned Exp, bool Overflow = (Exp >= sizeof(unsigned))>
struct float_pow_2 {
    static constexpr F value = 1u << Exp;
};
template<typename F, unsigned Exp>
struct float_pow_2<F,Exp,true> {
    static constexpr F half = float_pow_2<F,Exp/2>::value;
    static constexpr F value = half * half * (Exp & 1 ? 2 : 1);
};
悲喜皆因你 2024-08-26 11:04:46

如果 float 始终包含整数值,只需将其转换为 int:float_to_int = (unsigned long) 输入。

顺便说一句,77e12 溢出了一个浮点。双精度型将保存它,但随后您将需要此强制转换:(unsigned long long) 输入。

If the float always contains an integral value, just cast it to int: float_to_int = (unsigned long) input.

BTW, 77e12 overflows a float. A double will hold it, but then you'll need this cast: (unsigned long long) input.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文