I am writing my own long arithmetic library in C++ for fun and it is already pretty finished, I even implemented several Cryptogrphic algorithms with that library, but one important thing is still missing: I want to convert doubles (and floats/long doubles) into my number and vice versa. My numbers are represented as a variable sized array of unsigned long ints plus a sign bit.
I tried to find the answer with google, but the problem is that people rarely ever implement such things themselves, so I only find things about how to use Java BigInteger etc.
Conceptually, it is rather 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 set the exponent depending on my log2 function.
But I am having a hard time to figure out the details, I could either play around with some bit patterns and cast it to a double, but I didn’t find an elegant way to achieve that or I could “calculate” it by starting with 2, exponentiate, multiply etc, but that doesn’t seem very efficient.
I would appreciate a solution that doesn’t use any library calls because I am trying to avoid libraries for my project, otherwise I could just have used gmp, furthermore, I often have two solutions on several other occasions, one using inline assembler which is efficient and one that is more platform independent, so either answer is useful for 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 am willing to do some different implementations with some #ifdefs to achieve that.
I’m going to make non-portable assumption here: namely, that
unsigned long longhas more accurate digits thandouble. (This is true on all modern desktop systems that I know of.)First, convert the most significant integer(s) into an
unsigned long long. Then convert that to a doubleS. LetMbe the number of integers less than those used in that first step. multiplySby(1ull << (sizeof(unsigned)*CHAR_BIT*M). (If shifting more than 63 bits, you will have to split those into seperate shifts and do some alrithmetic) Finally, if the original number was negative you multiply this result by -1.This rounds a lot, but even with this rounding, due to the above assumption, no digits are lost that wouldn’t be lost anyway with the conversion to a double. I think this is a similar process to what Mark Ransom said, but I’m not certain.
For converting from a double to a biginteger, first seperate the mantissa into a
double Mand the exponent into anint E, usingfrexp. MultiplyMbyUNSIGNED_MAX, and store that result in anunsigned R. Ifstd::numeric_limits<double>::radix()is 2 (I don’t know if it is or not for x86/x64), you can easily shiftRleft byE-(sizeof(unsigned)*CHAR_BIT)bits and you’re done. Otherwise the result will instead beR*(E**(sizeof(unsigned)*CHAR_BIT))(where**means to the power of)If performance is a concern, you can add an overload to your bignum class for multiplying by
std::constant_integer<unsigned, 10>, which simply returns(LHS<<4)+(LHS<<2). You can similarly optimize other constants if you wish.