模乘(C语言)

模乘(C语言),c,math,multiplication,modulo,C,Math,Multiplication,Modulo,具体来说:我有两个无符号整数(a,b),我想计算(a*b)%UINT\u MAX(UINT\u MAX被定义为最大无符号整数)。最好的方法是什么 背景:我需要为linux编写一个模拟几何序列的模块,从中读取将得到下一个元素(modulo UINT_MAX),我找到的唯一解决方案是将当前元素添加到自身,而添加是使用以下逻辑完成的:(我用于算术序列) for(int i=0;i差){ 当前_值+=差值; }否则{ 当前_值=差值-(UINT_MAX-当前_值); } 当第一次迭代中的当前_值=a时

具体来说:我有两个无符号整数(a,b),我想计算(a*b)%UINT\u MAX(UINT\u MAX被定义为最大无符号整数)。最好的方法是什么

背景:我需要为linux编写一个模拟几何序列的模块,从中读取将得到下一个元素(modulo UINT_MAX),我找到的唯一解决方案是将当前元素添加到自身,而添加是使用以下逻辑完成的:(我用于算术序列)

for(int i=0;i差){
当前_值+=差值;
}否则{
当前_值=差值-(UINT_MAX-当前_值);
}
当第一次迭代中的当前_值=a时(并且在每次迭代中都会更新,而差值=a时(始终)。 显然,这不是一个明智的解决方案。 一个聪明的人如何做到这一点


谢谢!

如前所述,如果您有一种宽度是可用宽度两倍的类型,请在此处使用

(unsigned int)(((unsigned long long)a * b) % UINT_MAX)
如果
int
为32位且
long-long
64位(或更多)。如果没有更大的类型,则可以将因子拆分为位宽的一半,乘以并减少部分,最后进行组装。此处为32位无符号的示例:

a_low = a & 0xFFFF;  // low 16 bits of a
a_high = a >> 16;    // high 16 bits of a, shifted in low half
b_low = b & 0xFFFF;
b_high = b >> 16;
/*
 * Now a = (a_high * 65536 + a_low), b = (b_high * 65536 + b_low)
 * Thus a*b = (a_high * b_high) * 65536 * 65536
 *          + (a_high * b_low + a_low * b_high) * 65536
 *          + a_low * b_low
 *
 * All products a_i * b_j are at most (65536 - 1) * (65536 - 1) = UINT_MAX - 2 * 65536 + 2
 * The high product reduces to
 * (a_high * b_high) * (UINT_MAX + 1) = (a_high * b_high)
 * The middle products are a bit trickier, but splitting again solves:
 * m1 = a_high * b_low;
 * m1_low = m1 & 0xFFFF;
 * m1_high = m1 >> 16;
 * Then m1 * 65536 = m1_high * (UINT_MAX + 1) + m1_low * 65536 = m1_high + m1_low * 65536
 * Similar for a_low * b_high
 * Finally, add the parts and take care of overflow
 */
m1 = a_high * b_low;
m2 = a_low * b_high;
m1_low = m1 & 0xFFFF;
m1_high = m1 >> 16;
m2_low = m2 & 0xFFFF;
m2_high = m2 >> 16;
result = a_high * b_high;
temp = result + ((m1_low << 16) | m1_high);
if (temp < result)    // overflow
{
    result = temp+1;
}
else
{
    result = temp;
}
if (result == UINT_MAX)
{
    result = 0;
}
// I'm too lazy to type out the rest, you get the gist, I suppose.
a_low=a&0xFFFF;//a的低16位
a_high=a>>16;//a的高16位,在低半部分移位
b_low=b&0xFFFF;
b_high=b>>16;
/*
*现在a=(a_高*65536+a_低),b=(b_高*65536+b_低)
*因此a*b=(a_高*b_高)*65536*65536
*+(a_高*b_低+a_低*b_高)*65536
*+a_低*b_低
*
*所有产品a_i*b_j最多为(65536-1)*(65536-1)=UINT_MAX-2*65536+2
*高产品减少到
*(a_高*b_高)*(UINT_最大值+1)=(a_高*b_高)
*中间产品有点棘手,但拆分再次解决了以下问题:
*m1=a_高*b_低;
*m1_低=m1&0xFFFF;
*m1_高=m1>>16;
*然后m1*65536=m1_高*(UINT_MAX+1)+m1_低*65536=m1_高+m1_低*65536
*与a_低*b_高类似
*最后,添加部件并注意溢出
*/
m1=a_高*b_低;
m2=a_低*b_高;
m1_低=m1&0xFFFF;
m1_高=m1>>16;
m2_低=m2&0xFFFF;
m2_高=m2>>16;
结果=a_高*b_高;

temp=result+((m1_lowEDIT):如注释中所指出的……此答案适用于模MAX_INT+1 我会把它放在这里,以备将来参考

比这简单得多:

只需将两个无符号整数相乘,结果也将是一个无符号整数。 不符合无符号int的所有内容基本上都不存在。 因此无需进行模运算:

#包括
void main()
{
无符号整数a,b;
a=0x90000000;
b=2;
无符号整数c=a*b;
printf(“答案是%X\r\n”,c);
}

答案是:0x20000000(所以它应该是0x120000000,但是答案被截断了,这正是你想要的模运算)

你不允许使用模运算符或8字节整数类型吗?对于“long-long”比int长的类型,这是一个非常简单的愚蠢的解决方案。long-long结果=((long-long)a)*((long-long)b) %((long-long)UINT_MAX);@JoachimIsaksson结果不应该是long-long类型,对吗?你可能可以使用中国的余数定理。看到这些问题,我正在寻找一种解决方案,它也适用于系统中的“最大”数据类型,所以使用“long-long”是不是。@在这种情况下,ypercube CRT很难使用,因为UINT_MAX=(2^n)-1,不是吗?可以通过应用公式(a*n+b)%(n-1)=(a+b)%(n-1)简化无符号长模MAX_INT的减少是的,但是如果
a+b
溢出,你必须进行调整。如果有更大的类型可用,上下转换可以解决问题,而不会将产品分成高位和低位,因此在概念上更简单。同意它在概念上更简单,但技巧确实避免了昂贵的双字分割。虽然我不确定d在今天的处理器上,ivision仍然比shift、add和overflow check慢。但既然你对这类事情的了解比我多得多,我相信你的话。这里没有内部知识。但大多数32位处理器没有本机的“64除以32得到64”指令(它们可能有64 div 32到32)64 div 32至64案例通常在软件中实现。看起来您误读了问题和第一个答案。(现在已被删除)OP想要的是模
UINT\u MAX
,而不是
UINT\u MAX+1
a_low = a & 0xFFFF;  // low 16 bits of a
a_high = a >> 16;    // high 16 bits of a, shifted in low half
b_low = b & 0xFFFF;
b_high = b >> 16;
/*
 * Now a = (a_high * 65536 + a_low), b = (b_high * 65536 + b_low)
 * Thus a*b = (a_high * b_high) * 65536 * 65536
 *          + (a_high * b_low + a_low * b_high) * 65536
 *          + a_low * b_low
 *
 * All products a_i * b_j are at most (65536 - 1) * (65536 - 1) = UINT_MAX - 2 * 65536 + 2
 * The high product reduces to
 * (a_high * b_high) * (UINT_MAX + 1) = (a_high * b_high)
 * The middle products are a bit trickier, but splitting again solves:
 * m1 = a_high * b_low;
 * m1_low = m1 & 0xFFFF;
 * m1_high = m1 >> 16;
 * Then m1 * 65536 = m1_high * (UINT_MAX + 1) + m1_low * 65536 = m1_high + m1_low * 65536
 * Similar for a_low * b_high
 * Finally, add the parts and take care of overflow
 */
m1 = a_high * b_low;
m2 = a_low * b_high;
m1_low = m1 & 0xFFFF;
m1_high = m1 >> 16;
m2_low = m2 & 0xFFFF;
m2_high = m2 >> 16;
result = a_high * b_high;
temp = result + ((m1_low << 16) | m1_high);
if (temp < result)    // overflow
{
    result = temp+1;
}
else
{
    result = temp;
}
if (result == UINT_MAX)
{
    result = 0;
}
// I'm too lazy to type out the rest, you get the gist, I suppose.
 #include <stdio.h>

 void main()
 {
     unsigned int a,b;
     a = 0x90000000;
     b = 2;

     unsigned int c = a*b;

     printf("Answer is %X\r\n", c);
 }