Convert Big Integer & # 8596; double in C ++

I write my own long arithmetic library in C ++ for fun, and it is already pretty finished, I even implemented several cryptographic algorithms with this library, but one important thing is still missing: I want to convert doubles (and floats / long doubles) to my number and vice versa. My numbers are represented as an array with a variable size of an unsigned long int plus a signed bit.

I tried to find the answer using Google, but the problem is that people rarely ever implement such things, so I only find things about how to use Java BigInteger, etc.

Conceptually, this is quite easy: I take the mantissa, shift it by the number of bits dictated by the exponent, and set the sign. In the other direction, I truncate it so that it fits into the mantissa and sets the exponent depending on my function log2.

But it’s hard for me to figure out the details, I could either play with some bit patterns or throw it on a double, but I couldn’t find an elegant way to achieve this, or I could “calculate” it, exponentially, multiply it, etc. .d., but it does not seem very effective.

I would appreciate a solution that does not use library calls, because I try to avoid libraries for my project, otherwise I could just use gmp, in addition, I often have two solutions in several other cases, one of which is used inline assembler, which is efficient and more platform independent, so any answer is useful to me.

edit: I use uint64_t for my parts, but I would like to be able to change it depending on the machine, but I want to make several different implementations with some #ifdefs to achieve this.

+4
source share
4 answers

I'm going to make an intolerable assumption here: namely, that unsigned long long has more accurate digits than double . (This is true for all modern desktop systems that I know of.)

First, convert the most significant integer (s) to unsigned long long . Then convert it to double S Let M be the number of integers less than those used in this first step. multiply S by (1ull << (sizeof(unsigned)*CHAR_BIT*M) . (If you shift more than 63 bits, you will have to divide them into separate shifts and make some algorithms.) Finally, if the original number was negative, you multiply this result by -1.

This round is a lot, but even with this rounding, due to the above assumption, no digits are lost that will not be lost in any case with conversion to double. I think this is a similar process that Mark Ransome talked about, but I'm not sure.

To convert from double to biginteger, first select the mantissa in double M and the exponent in int E using frexp . Multiply M by UNSIGNED_MAX and save the result in unsigned R If std::numeric_limits<double>::radix() is 2 (I don't know if it is or not for x86 / x64), you can easily shift R left using the E-(sizeof(unsigned)*CHAR_BIT) bit, and you're done. Otherwise, the result will be R*(E**(sizeof(unsigned)*CHAR_BIT)) (where ** means power)

If performance is a problem, you can add overloading to your bignum class to multiply by std::constant_integer<unsigned, 10> , which simply returns (LHS<<4)+(LHS<<2) . You can also optimize other constants if you want.

+2
source

This blog post can help you Explain and Optimize Integer -> asFloat

Otherwise, you may still have an idea of ​​an algorithm with this question. SO Converting from unsigned long long to float with rounding to the nearest even

+1
source

To move from a large integer to a double, just do it the same way you parse numbers. For example, you analyze the number "531" as "1 + (3 * 10) + (5 * 100)". Calculate each part using doubles, starting with the least significant part.

To go from double to large, do the same, but in the opposite direction, starting with the most significant part. So, to convert 531, you first see that it is more than 100, but less than 1000. You will find the first digit by dividing it by 100. Then you subtract to get the remainder 31. Then find the next digit by dividing it by 10. And and etc.

Of course, you will not use dozens (unless you store your large integers as numbers). Exactly how you break it up depends on how your large integer class is built. For example, if it uses 64-bit units, then you will use 2 ^ 64 powers instead of 10 powers.

0
source

You do not speak explicitly, but I assume that your library is an integer, and unsigned longs are 32-bit and binary (not decimal). Converting to double is simple, so I’ll do it first.

Start with a multiplier for the current piece; if the number is positive, it will be 1.0, if -1 is negative. For each of the unsigned long ints in your bignum, multiply by the current factor and add it to the result, then multiply the factor by pow(2.0, 32) (4294967296.0) by 32 bits or pow(2.0, 64) (18446744073709551616.0) by 64 bits.

You can optimize this process by working with only the two most significant values. You need to use 2, even if the number of bits in your integer type is greater than the precision of the double, since the number of bits used in the most significant value can be only 1. You can generate a multiplier using the power of 2 to the number of skipped bits, for example pow(2.0, most_significant_count*sizeof(bit_array[0])*8) . You cannot use a bit shift, as indicated in another answer, because it will overflow after the first value.

To convert from a double, you can get the exponent and the mantissa separated by the frexp function. The mantissa will have a floating point value between 0.5 and 1.0, so you want to multiply it by pow (2.0, 32) or pow (2.0, 64) to convert it to an integer, and then set the exponent to -32 or -64 to compensate.

0
source

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


All Articles