C++ 按双精度缩放大整数
我需要将几百位的大整数按双精度进行缩放。特别是,我需要计算 M*系数mod M 其中M是一个大整数,因子是双精度的。我不会使用任何库,除非您想将头文件中的十几行代码称为“库”;因此,大浮点数学在这里不是一个选项 Knuth和/MPIR源代码没有答案,在这里我只找到了不适用的答案,因为第二个答案太奇怪了,第一个答案失去了太多的精度 从第一原理出发,用uint64模拟大整数,我用64位VC++或gcc/MinGW64实现了这个可运行:C++ 按双精度缩放大整数,c++,floating-point,biginteger,arbitrary-precision,C++,Floating Point,Biginteger,Arbitrary Precision,我需要将几百位的大整数按双精度进行缩放。特别是,我需要计算 M*系数mod M 其中M是一个大整数,因子是双精度的。我不会使用任何库,除非您想将头文件中的十几行代码称为“库”;因此,大浮点数学在这里不是一个选项 Knuth和/MPIR源代码没有答案,在这里我只找到了不适用的答案,因为第二个答案太奇怪了,第一个答案失去了太多的精度 从第一原理出发,用uint64模拟大整数,我用64位VC++或gcc/MinGW64实现了这个可运行: #include <cassert> #includ
#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));
}
乘法和移位将在我的应用程序中使用大整数数学完成,但原理应该是相同的
基本方法正确吗?或者测试工作仅仅是因为我在这里使用玩具整数进行测试?我对浮点数学一窍不通,我从一个
澄清:从乘法开始的所有操作都将使用部分大整数数学;在这里,我只是为了得到一个可以发布并实际运行的小玩具程序而使用limb_t。最终的应用程序将使用GMP的mpn_mul_1和mpn_rshift的道德等价物。浮点数只不过是三个术语的乘积。这三个术语是符号、有效位(有时称为尾数)和指数。这三项的值计算如下: -1符号*有效位*基本指数
基通常为2,虽然C++标准不能保证。相应地,您的计算变得
M*系数mod M ==M*-1符号*有效位*基指数模M ==-1信号+符号*absM*有效位*baseexponent mod M 计算结果的符号应该相当简单。计算X*baseexponent相当直截了当:如果基数为2,则它是一个合适的位移位,或者是一个与基数左移位的幂相乘/除法,或者是正指数的乘法,负指数的右移位或除法。假设您的大整数表示已经支持模运算,唯一有趣的术语是absM*有效位的乘法,但这只是一个普通的整数乘法,尽管对于大整数表示。我没有仔细检查,但我认为这是你链接到的第一个答案与你描述的太奇异的答案之间的联系 剩下的位是从双精度计数器中提取符号、有效位和指数。通过与0.0进行比较,可以很容易地确定符号,使用frexp可以获得有效位和指数,例如参见。有效位以双精度返回,也就是说,您可能希望将其乘以2std::numeric_limits::digits并适当调整指数。我已经有一段时间没有这样做了,也就是说,我不完全确定frexp的extact契约回答您的问题:我不熟悉您正在使用的GMP操作,但我认为您确实执行了上述计算。我不完全确定这个问题是否有意义。假设M是17,因子是10^500。因此,双精度的精度不够高,因为双精度的单位是有效的,因此没有希望计算出正确的答案。对于正在考虑的问题-M*f mod M-结果必须介于0和M-1之间。如果有人通过10^500,那么他们将获得他们应得的精度。这就是说,根据指示的更改(即丢失modf调用并处理从frexp返回正指数的情况),大整数应正确放大。注意:limb_t hi on的部分将是大整数math.frexp返回标准化的有效位和基2的适当指数;i、 e.0.5将原封不动地返回,指数将为0。这里,它用于从因子中提取所有有效位。我添加modf调用是为了避免大整数模运算;我的应用程序的其余部分只使用小的肢体大小的模和大整数,这与完整的大整数除法相比,实现起来很简单。我认为应该注意的是,根据标准,double没有一个固定且定义良好的大小。通常是64位,但也有其他情况下,双精度可以更大,我没有遇到双精度小于64位的情况,但根据标准,后者仍然是一种可能性,尽管据我所知,基本上没有体系结构实现小于64位的双精度。您必须检查double和float的大小,因为该大小将影响浮点表示的精度。@DarthGizka:是的,有效位以分数形式返回-但是,这是您不想要的!它需要作为一个整数,也就是说,你要缩放它,没有
非零小数位,并转换为合适的整数。当乘以有效位时,需要相应调整指数。我注意到我没有完全回答您的问题,并补充道:我认为您执行的计算与上面描述的计算完全相同。@user2485710://精度将是肢体位和DBL位的下限_DIG@DarthGizka您可能希望切换到头文件并使用std::numeric\u limits,看一下成员常量和成员函数,它将对C++用户来说更加地道。