Convert float to bigint (aka portable way to get binary exponent and mantissa)

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

I would like to convert large floats or double numbers to bigint. I have a working method, but it’s a bit hacked. I used the IEEE 754 number specification to get the binary sign, mantissa, and input number metric.

Here is the code (The sign is ignored here, it doesn’t matter):

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 pretty 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 a binary mantissa and an exponent from a float or double?


Thanks for answers. For posterity, this is a solution using frexp. It is less efficient due to the loop, but it works the same for float and double, doesn't use reinterpret_cast or depends on any knowledge about floating point 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; } 
+4
source share
3 answers

You cannot retrieve values ​​with frexp (), frexpf (), frexpl () ?

+8
source

I like your decision! This led me to the right path.

I would recommend one thing: why not get a bunch of bits right away and almost always exclude any loop? I implemented a float-to-bigint function, for example:

 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 a simple way to place two-constant floating point 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); }; 
+1
source

If float always contains an integer value, just enter it in int: float_to_int = (unsigned long) input.

BTW, 77e12 overflows float. A double will hold it, but then you need this text: (unsigned long).

-one
source

Source: https://habr.com/ru/post/1299339/


All Articles