我正在用C ++编写我自己的长算术库以获得乐趣,它已经完成了,我甚至用该库实现了几个Cryptogrphic算法,但仍然缺少一个重要的事情:我想转换双精度(和浮点数/长双打)到我的号码,反之亦然。我的数字表示为一个可变大小的无符号长整数数组加上一个符号位。
我试图用谷歌找到答案,但问题是人们很少自己实现这些东西,所以我只找到关于如何使用Java BigInteger等的东西。
从概念上讲,它很容易:我采用尾数,将其移位指数所指定的位数并设置符号。在另一个方向,我截断它,使其适合尾数,并根据我的log2函数设置指数。
但是我很难弄清楚细节,我可以玩一些模式并将其转换成双重模式,但我没有找到一种优雅的方法来实现这一点,或者我可以“计算”它以2开始,取幂,乘以等,但这看起来效率不高。
我很感激一个不使用任何库调用的解决方案因为我试图避免使用我的项目的库,否则我可能只是使用了gmp,而且,我经常在其他几个场合有两个解决方案,一个使用内联汇编程序是高效的,而且更加独立于平台,因此任何一个答案对我都有用。
编辑:我使用uint64_t作为我的部件,但我希望能够根据机器改变它,但我愿意用一些#ifdefs做一些不同的实现来实现它。
答案 0 :(得分:2)
我将在此处进行非便携式假设:即,unsigned long long
的数字比double
更准确。 (在我所知的所有现代桌面系统上都是如此。)
首先,将最重要的整数转换为unsigned long long
。然后将其转换为双S
。设M
为小于第一步中使用的整数的整数。将S
乘以(1ull << (sizeof(unsigned)*CHAR_BIT*M)
。 (如果移位超过63位,则必须将它们分成单独的移位并进行一些算术运算)最后,如果原始数字是负数,则将此结果乘以-1。
这很多,但即使有了这个四舍五入,由于上面的假设,没有数字丢失,无论如何转换为双倍都不会丢失。我认为这与Mark Ransom所说的类似,但我不确定。
要从double转换为biginteger,请先使用double M
将尾数分隔为int E
,将指数分隔为frexp
。将M
乘以UNSIGNED_MAX
,并将结果存储在unsigned R
中。如果std::numeric_limits<double>::radix()
为2(我不知道是否为x86 / x64),您可以轻松地将R
向左移E-(sizeof(unsigned)*CHAR_BIT)
位并完成。否则结果将是R*(E**(sizeof(unsigned)*CHAR_BIT))
(其中**
意味着权力)
如果需要考虑性能,可以在bignum类中添加一个重载,乘以std::constant_integer<unsigned, 10>
,只需返回(LHS<<4)+(LHS<<2)
。如果愿意,您可以类似地优化其他常量。
答案 1 :(得分:1)
此博客文章可能会帮助您Clarifying and optimizing Integer>>asFloat
否则,您可以通过此SO问题Converting from unsigned long long to float with round to nearest even
了解算法答案 2 :(得分:0)
要从大整数变为双精度,只需按照解析数字的方式进行操作即可。例如,您将数字“531”解析为“1 +(3 * 10)+(5 * 100)”。使用双精度计算每个部分,从最不重要的部分开始。
要从双整数变为大整数,请以相同的方式进行,但从最重要的部分开始反向。因此,要转换531,您首先会看到它超过100但小于1000.您可以通过除以100找到第一个数字。然后减去得到31的余数。然后通过除以10找到下一个数字。等等。
当然,你不会使用数十(除非你将大整数存储为数字)。究竟如何分解它取决于你的大整数类的构造方式。例如,如果它使用64位单位,那么你将使用2 ^ 64而不是10的幂。
答案 3 :(得分:0)
您没有明确说明,但我假设您的库只是整数,无符号长整数是32位和二进制(不是十进制)。转换到的转换很简单,所以我先解决这个问题。
从当前作品的乘数开始;如果数字是正数,它将是1.0,如果是负数,它将是-1.0。对于bignum中的每个无符号长整数,乘以当前乘数并将其添加到结果中,然后将乘数乘以pow(2.0, 32)
(4294967296.0)(32位)或pow(2.0, 64)
(18446744073709551616.0)(64)位。
您可以通过仅处理2个最重要的值来优化此过程。即使整数类型中的位数大于double的精度,也需要使用2,因为最重要值中使用位的数量可能只为1.您可以生成乘以2乘以跳过的位数,例如pow(2.0, most_significant_count*sizeof(bit_array[0])*8)
。您不能使用另一个答案中给出的位移,因为它会在第一个值之后溢出。
要从double转换,您可以使用frexp
函数将指数和尾数相互分开。尾数将作为0.5到1.0之间的浮点值,因此您需要将它乘以pow(2.0,32)或pow(2.0,64)将其转换为整数,然后将指数调整为-32或-64补偿。