我需要用双精度来扩展大整数(几百位)。特别是,我需要计算
(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()的道德等同物。
答案 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操作,但我认为你所做的操作确实执行了上述计算。