将一个大整数缩放一倍
Scaling a big integer by a double
我需要将大整数(几百位)缩放一倍。特别是,我需要计算
(M*因子)mod M
其中M是一个大整数,因子为一个二重。我不会使用任何库,除非你想把头文件中的十几行代码称为"库";因此,大浮动数学在这里不是一个选择。
Knuth和GMP/MPIR源代码没有答案,在这里我只找到了大整数和二重之间的乘法,这并不适用,因为第二个答案太奇特,第一个答案失去了太多精度。
根据第一性原理,用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++引用中选择了函数。
澄清:从乘法开始的所有运算都将使用(偏)大整数数学;在这里,我只是使用limb_t来实现这个目的,以便获得一个可以发布并实际运行的小玩具程序。最后一个应用程序将使用GMP的mpn_mul_1()和mpn_rshift()的道德等价物。
浮点数只不过是三项的乘积。这三项是符号、有效位(有时称为尾数)和指数。这三项的值计算为
(-1)符号*有效位*基数指数
基数通常是2,尽管C++标准不能保证这一点。相应地,您的计算变成
(M*因子)modM
==(M*(-1)符号*有效位*基数指数)modM
==((-1)符号(M)+符号*abs(M*有效位*基数指数)modM
计算结果的符号应该相当琐碎。计算X*base指数是相当直接的:如果base是2,则它是合适的位移位,或者是与base的幂相乘/除法(对于正幂,左移或乘法,对于负指数则右移或除法)。假设你的大整数表示已经支持模运算,唯一有趣的项是abs(M)*有效位的乘积,但这只是一个正常的整数乘法,尽管对于你的大整型表示。我没有仔细检查过,但我认为这是你联系到的第一个答案(你形容为"太异国情调"的答案)。
剩余位用于从double
中提取符号、有效位和指数。符号可以通过与0.0
的比较很容易地确定,有效位和指数可以使用frexp()
获得(例如,请参见此答案)。有效位作为double
返回,即您可能希望将其乘以2std::numeric_limits<double>::digits
并适当调整指数(我已经有一段时间没有这样做了,即我不完全确定frexp()
的外部契约)。
回答你的问题:我不熟悉你正在使用的GMP操作,但我认为你所做的操作确实执行了上述计算。
- UE4-如何在给定4个屏幕坐标的情况下缩放纹理或材质
- 计算缩放多边形的比例,得到给定的多边形面积
- QwtPlot具有相等的轴和自动缩放
- 如何在directx/c++中进行平移/缩放操作
- 在Qt中实现无限可缩放的画布
- 是否可以为 QPixmap 派生类嵌入缩放方法?
- 形状对象的旋转和缩放不正确C++
- 如何在 OpenSceneGraph 中缩放/旋转/移动资产
- 如何在OpenGL(GLFW,很高兴)中进行2D缩放?
- 改变或缩放两个正态分布以具有特定的相关系数
- SDL2 调整窗口大小后如何缩放鼠标坐标?
- 如何在OpenGL中正确旋转和缩放对象?
- 仅当类型为 std::complex 时,才进行缩放
- 将 VS Code 用于跨平台可缩放C++项目
- 根据帧速率缩放/缩小数字
- 在 c++ 中缩放浮点值
- 如何防止 DirectX C++程序的拉伸/缩放
- 将一个大整数缩放一倍
- 在C++中缩放两倍时的精度差异
- 在direct3D9中有没有一种方法可以通过最近的邻居来缩放精灵