在C语言中,如何在硬件有限的无符号整数上实现模运算
我有一台只支持32位操作的机器,long long不能在这台机器上工作。 我有一个64位的量,表示为两个无符号int 32。 问题是我如何用32位除数对64位量执行mod r=a模b 其中: a是64位值,b是32位值 我想我可以通过以下方式代表国防部: a=a1*(2^32)+a2(其中a1为顶部位,a2为底部位) (a1*(2^32)+a2)模b=(a1*2^32)模b+a2模b)模b (a1*2^32)模b+a2模b)模b=(a1模b*2^32模b+a2模b)模b在C语言中,如何在硬件有限的无符号整数上实现模运算,c,algorithm,math,numbers,C,Algorithm,Math,Numbers,我有一台只支持32位操作的机器,long long不能在这台机器上工作。 我有一个64位的量,表示为两个无符号int 32。 问题是我如何用32位除数对64位量执行mod r=a模b 其中: a是64位值,b是32位值 我想我可以通过以下方式代表国防部: a=a1*(2^32)+a2(其中a1为顶部位,a2为底部位) (a1*(2^32)+a2)模b=(a1*2^32)模b+a2模b)模b (a1*2^32)模b+a2模b)模b=(a1模b*2^32模b+a2模b)模b 但问题是2^32模b有时
但问题是2^32模b有时可能等于2^32,因此乘法会溢出。我已经尝试将乘法转换为加法,但这也要求我使用2^32,如果我再次修改,它将给我2^32:),因此我不确定如何使用32位1执行64位值的无符号修改 我想一个简单的解决方案是执行以下操作:
就像用铅笔和纸做长除法一样
#include <stdio.h>
unsigned int numh = 0x12345678;
unsigned int numl = 0x456789AB;
unsigned int denom = 0x17234591;
int main() {
unsigned int numer, quotient, remain;
numer = numh >> 16;
quotient = numer / denom;
remain = numer - quotient * denom;
numer = (remain << 16) | (numh & 0xffff);
quotient = numer / denom;
remain = numer - quotient * denom;
numer = (remain << 16) | (numl >> 16);
quotient = numer / denom;
remain = numer - quotient * denom;
numer = (remain << 16) | (numl & 0xffff);
quotient = numer / denom;
remain = numer - quotient * denom;
printf("%X\n", remain);
return 0;
}
#包括
无符号整数numh=0x12345678;
无符号整数numl=0x456789AB;
无符号整数denom=0x17234591;
int main(){
无符号整数,商,余数;
numer=numh>>16;
商=数/名;
剩余=数值-商*denom;
数值=(保留16);
商=数/名;
剩余=数值-商*denom;
numer=(保持有效:使用1000M随机组合对64位%
进行测试
像小学长除法a/b
(但在基数2中),如果可能,从a
中减去b
,然后移位,循环64次。返回剩余部分
#define MSBit 0x80000000L
uint32_t mod32(uint32_t a1 /* MSHalf */, uint32_t a2 /* LSHalf */, uint32_t b) {
uint32_t a = 0;
for (int i = 31+32; i >= 0; i--) {
if (a & MSBit) { // Note 1
a <<= 1;
a -= b;
} else {
a <<= 1;
}
if (a1 & MSBit) a++;
a1 <<= 1;
if (a2 & MSBit) a1++;
a2 <<= 1;
if (a >= b)
a -= b;
}
return a;
}
您的硬件的C编译器是否还不支持此功能?例如,uint64\u t a,b,r;r=a%b;
“2^32 mod b有时可能等于2^32”.建议不要。b考虑到a
是一个用基数2表示的位置数字系统中的数字,你确实知道有什么好处,还有什么问题——还不错。那么基数2呢⁶? 2¹⁶±1?(中文怎么办?)“不能做a2^32模b
”-->正确,但诀窍是2^32模b
=(2^32-b)mod b
。我们知道从2^32
中至少可以减去1b
,而结果不会变为负值。循环一次移位1位,这就是所需的全部。2^32 mod b!=2^32,因为机器上的b<2^32。顺便说一句:不要得到与printf相同的答案(“llX\n”,0x123456789abu%0x17234591u)“一点" way@crux请您澄清这是返回uint32\u t还是int32\u t?是的,谢谢。返回值应该是uint32\u t
,而不是int32\u t
。答案已更新。请您详细说明您编写的注释,我发现很难理解。如果您有0x8000000,并且您离开了班次by 1则会得到0(在uint32上),因此信息丢失。我知道您可以判断是否会发生这种情况,但我不知道修复方法是什么。(如果a和b都设置了顶部位,但其余位不同,则会出现这种情况)这是因为您知道a和b的顶层位都已设置,您正在利用这一点吗?@Har True,当设置a
MSbit时,左移位将丢失该位,但随后代码位于“知道”MSbit已移位的if(){}
块内。因此,“33位”a
是0x1\u 0000\u 0000+a
。因为它大于b
,所以代码相减。无符号整数数学在C中定义得很好,可以“环绕”因此,a-=b;
的结果在这里在数学上是正确的,因为它与a=0x1\u 0000\u 0000+a-b的结果相同。在我看来,下面的通用代码做得更好。@Har涉及无符号操作数的计算永远不会溢出,因为不能由结果无符号整数类型表示的结果将被减少为大于结果类型可以表示的最大值一的数的模。“C11dr§6.2.5 9低,32位减法的结果是a-b mod 0x1\u 0000\u 0000
。
#include <stdint.h>
#include <limits.h>
// Use any unpadded unsigned integer type
#define UINT uint32_t
#define BitSize (sizeof(UINT) * CHAR_BIT)
#define MSBit ((UINT)1 << (BitSize - 1))
UINT modu(const UINT *aarray, size_t alen, UINT b) {
UINT r = 0;
while (alen-- > 0) {
UINT a = aarray[alen];
for (int i = BitSize; i > 0; i--) {
UINT previous = r;
r <<= 1;
if (a & MSBit) {
r++;
}
a <<= 1;
if ((previous & MSBit) || (r >= b)) {
r -= b;
}
}
}
return r;
}
UINT modu2(UINT a1 /* MSHalf */, UINT a2 /* LSHalf */, UINT b) {
UINT a[] = { a2, a1 }; // Least significant at index 0
return modu(a, sizeof a / sizeof a[0], b);
}