注意:这个问题与
Fastest way to calculate a 128-bit integer modulo a 64-bit integer不同.
这是一个C#小提琴:
https://dotnetfiddle.net/QbLowb
鉴于伪代码:
UInt64 a = 9228496132430806238;
UInt32 d = 585741;
我怎么算
UInt32 r = a % d?
当然,问题在于我不在支持UInt64数据类型的编译器中.但我可以访问Windows ULARGE_INTEGER
union:
typedef struct ULARGE_INTEGER {
DWORD LowPart;
DWORD HighPart;
};
这意味着我可以将上面的代码转换为:
//9228496132430806238 = 0x80123456789ABCDE
UInt32 a = 0x80123456; //high part
UInt32 b = 0x789ABCDE; //low part
UInt32 r = 585741;
怎么做
但现在来了如何进行实际计算.我可以从铅笔纸长分开始:
________________________
585741 ) 0x80123456 0x789ABCDE
为了简化,我们可以使用变量:
现在我们完全使用32位无符号类型,我的编译器支持这种类型.
u1 = a / r; //integer truncation math
v1 = a % r; //modulus
但是现在我让自己陷入了停滞状态.因为现在我必须计算:
v1||b / r
换句话说,我必须执行64位值的划分,这是我首先无法执行的!
这已经是一个已经解决的问题.但我能在Stackoverflow上找到的唯一问题是人们试图计算:
a^b mod n
或其他加密大型多精度运算或近似浮点运算.
奖金阅读
> Microsoft Research: Division and Modulus for Computer Scientists
> https://stackoverflow.com/questions/36684771/calculating-large-mods-by-hand
> Fastest way to calculate a 128-bit integer modulo a 64-bit integer(无关的问题;我恨你的人)
1它确实支持Int64,但我不认为这有助于我
使用Int64支持
我希望在没有本机64位支持的编译器中针对ULARGE_INTEGER(甚至LARGE_INTEGER)执行模数的通用解决方案.这将是正确的,良好的,完美的和理想的答案,其他人可以在需要时使用.
但也有我遇到的问题的现实.它可以导致一个通常对其他人没用的答案:
>通过拨打one of the Win32 large integer functions作弊(虽然模数没有)
>使用64位支持对有符号整数进行欺骗
我可以检查一下是否是积极的.如果是,我知道我的编译器对Int64的内置支持将处理:
UInt32 r = a % d; //for a >= 0
然后就是如何处理另一种情况:a是否定的
UInt32 ModU64(ULARGE_INTEGER a, UInt32 d)
{
//Hack: Our compiler does support Int64, just not UInt64.
//Use that Int64 support if the high bit in a isn't set.
Int64 sa = (Int64)a.QuadPart;
if (sa >= 0)
return (sa % d);
//sa is negative. What to do...what to do.
//If we want to continue to work with 64-bit integers,
//we could now treat our number as two 64-bit signed values:
// a == (aHigh + aLow)
// aHigh = 0x8000000000000000
// aLow = 0x0fffffffffffffff
//
// a mod d = (aHigh + aLow) % d
// = ((aHigh % d) + (aLow % d)) % d //<--Is this even true!?
Int64 aLow = sa && 0x0fffffffffffffff;
Int64 aHigh = 0x8000000000000000;
UInt32 rLow = aLow % d; //remainder from low portion
UInt32 rHigh = aHigh % d; //this doesn't work, because it's "-1 mod d"
Int64 r = (rHigh + rLow) % d;
return d;
}
回答
花了一段时间,但我终于得到了答案.我会发布它作为答案;但Z29kIGZ1Y2tpbmcgZGFtbiBzcGVybSBidXJwaW5nignvY2tzdWNraW5nIHR3YXR3YWZmbGVz人们错误地认定我的独特问题完全重复.
UInt32 ModU64(ULARGE_INTEGER a, UInt32 d)
{
//I have no idea if this overflows some intermediate calculations
UInt32 Al = a.LowPart;
UInt32 Ah = a.HighPart;
UInt32 remainder = (((Ah mod d) * ((0xFFFFFFFF - d) mod d)) + (Al mod d)) mod d;
return remainder;
}
最佳答案 我刚刚在这个相关的QA中更新了我的ALU32类代码:
> Cant make value propagate through carry
作为mul的CPU程序集独立代码,请求了div.分频器正在解决你所有的问题.然而,它使用二进制长除法,因此它比堆叠32位mul / mod / div操作有点宽松.这里是代码的相关部分:
void ALU32::div(DWORD &c,DWORD &d,DWORD ah,DWORD al,DWORD b)
{
DWORD ch,cl,bh,bl,h,l,mh,ml;
int e;
// edge cases
if (!b ){ c=0xFFFFFFFF; d=0xFFFFFFFF; cy=1; return; }
if (!ah){ c=al/b; d=al%b; cy=0; return; }
// align a,b for binary long division m is the shifted mask of b lsb
for (bl=b,bh=0,mh=0,ml=1;bh<0x80000000;)
{
e=0; if (ah>bh) e=+1; // e = cmp a,b {-1,0,+1}
else if (ah<bh) e=-1;
else if (al>bl) e=+1;
else if (al<bl) e=-1;
if (e<=0) break; // a<=b ?
shl(bl); rcl(bh); // b<<=1
shl(ml); rcl(mh); // m<<=1
}
// binary long division
for (ch=0,cl=0;;)
{
sub(l,al,bl); // a-b
sbc(h,ah,bh);
if (cy) // a<b ?
{
if (ml==1) break;
shr(mh); rcr(ml); // m>>=1
shr(bh); rcr(bl); // b>>=1
continue;
}
al=l; ah=h; // a>=b ?
add(cl,cl,ml); // c+=m
adc(ch,ch,mh);
}
cy=0; c=cl; d=al;
if ((ch)||(ah)) cy=1; // overflow
}
查看链接的QA以获取类的描述和使用的子功能. a / b背后的想法很简单:
>定义
假设我们得到64/64位除法(模数将是部分乘积)并且想要使用32位算术,所以:
(ah,al) / (bh,bl) = (ch,cl)
每个64位QWORD将被定义为高和低32位DWORD.
>对齐a,b
就像纸上的计算部门一样,我们必须对齐b所以它除了一个如此找到sh:
(bh,bl)<<sh <= (ah,al)
(bh,bl)<<(sh+1) > (ah,al)
并计算m
(mh,ml) = 1<<sh
请注意,如果bh> = 0x80000000停止转移或我们将溢出…
>分裂
设置结果c = 0,然后简单地从a b> = a中减去b.对于每个减法,将m加到c.一旦b> a,则移位b,m再次对齐.如果m == 0或a == 0则停止.
>结果
c将保持64位的除法结果所以使用cl和类似a保持余数所以使用al作为你的模数结果.如果不发生溢出,你可以检查ch,ah是否为零(结果大于32位).对于像零除的边缘情况一样……
现在你想要64位/ 32位只需设置bh = 0 …为此我需要64位操作(, – ,<<,>>),我通过堆叠32位操作与Carry(即为什么我的ALU32类是在第一个创建的原因)更多信息请参阅上面的链接.