将一个大整数缩放一倍

时间:2014-10-18 14:06:27

标签: c++ floating-point biginteger arbitrary-precision

我需要用双精度来扩展大整数(几百位)。特别是,我需要计算

(M * factor)mod M

其中 M 是一个大整数,因子是一个双精度数。除非你想在头文件中调用十几行代码,否则我不会使用任何库。&#39 ;;因此大浮动数学不是一个选择。

Knuth和GMP / MPIR源代码没有答案,在这里我发现只有Multiplication between big integers and doubles不适用,因为第二个答案太奇特了,第一个答案太精确了。 / p>

使用uint64_t从第一原理开始工作并模拟大整数我想出了这个(用64位VC ++或gcc / MinGW64运行):

#include <cassert>
#include <cfloat>
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstdio>

#include <intrin.h>   // VC++, MinGW

#define PX(format,expression)  std::printf("\n%35s  ==  " format, #expression, expression);

typedef std::uint64_t limb_t;

// precision will be the lower of LIMB_BITS and DBL_MANT_DIG
enum {  LIMB_BITS = sizeof(limb_t) * CHAR_BIT  };

// simulate (M * factor) mod M with a 'big integer' M consisting of a single limb
void test_mod_mul (limb_t modulus, double factor)
{
   assert( factor >= 0 );

   // extract the fractional part of the factor and discard the integer portion

   double ignored_integer_part;
   double fraction = std::modf(factor, &ignored_integer_part);

   // extract the significand (aligned at the upper end of the limb) and the exponent

   int exponent;
   limb_t significand = limb_t(std::ldexp(std::frexp(fraction, &exponent), LIMB_BITS));

   // multiply modulus and single-limb significand; the product will have (n + 1) limbs

   limb_t hi;
/* limb_t lo = */_umul128(modulus, significand, &hi);

   // The result comprises at most n upper limbs of the product; the lowest limb will be 
   // discarded in any case, and potentially more. Factors >= 1 could be handled as well,
   // by dropping the modf() and handling exponents > 0 via left shift.

   limb_t result = hi;

   if (exponent)
   {
      assert( exponent < 0 );

      result >>= -exponent;
   }

   PX("%014llX", result);
   PX("%014llX", limb_t(double(modulus) * fraction));
}

int main ()
{
   limb_t const M = 0x123456789ABCDEull;  // <= 53 bits (for checking with doubles)

   test_mod_mul(M, 1 - DBL_EPSILON);
   test_mod_mul(M, 0.005615234375);
   test_mod_mul(M, 9.005615234375);
   test_mod_mul(M, std::ldexp(1, -16));
   test_mod_mul(M, std::ldexp(1, -32));
   test_mod_mul(M, std::ldexp(1, -52));
}

乘法和移位将在我的应用程序中使用大整数数学完成,但原理应该是相同的。

基本方法是正确的还是测试工作只是因为我在这里测试玩具整数?我不知道关于浮点数学的第一件事,我从C++ reference中选择了函数。

澄清:从乘法开始的一切都将用(部分)大整数数学完成;在这里我只是为了这个目的而使用limb_t来获得一个可以发布并实际运行的小玩具程序。最终的应用程序将使用GMP&lt; mpn_mul_1()和mpn_rshift()的道德等同物。

1 个答案:

答案 0 :(得分:4)

浮点数只不过是三个术语的乘积。三个术语是符号有效符号(有时称为尾数)和指数。这三个术语的值计算为

  

( - 1) sign * 有效数字 * base exponent

基数通常为2,尽管C ++标准并不能保证。相应地,您的计算变为

  

M * 因素)mod M

     

==( M *( - 1) 签署 * 有效数字 * base exponent )mod M

     

==((-1)符号( M )+ 符号 * abs( M )* 有效数 * base 指数 )mod M

计算结果的符号应该是微不足道的。计算 X * base exponent 非常简单:如果基数为2,则它是一个合适的位移位或者是/除以基数的幂(左移或乘法为正指数,右移或除以负指数)。假设你的大整数表示已经支持模运算,唯一有趣的术语是abs( M )* 有效数和的乘法,但这只是一个正常的整数乘法,尽管你的大整数表示。我没有仔细检查过,但我认为这就是你联系到的第一个答案(你所描述的那个&#34;太异国情调&#34;)。

剩下的部分是从double中提取 sign 有效数字 exponent 。通过与0.0有效数字的比较可以轻松确定符号,并且可以使用frexp()获取 exponent (请参阅{{3} } 例如)。 有效数字double的形式返回,即您可能希望将其乘以2 std::numeric_limits<double>::digits 并适当调整指数(我没有&# 39;暂时不做这件事,即我不完全确定frexp())的签订合同。

回答你的问题:我不熟悉你正在使用的GMP操作,但我认为你所做的操作确实执行了上述计算。