具体来说:我有两个无符号整数(a,b),我想计算(a * b)%UINT_MAX(UINT_MAX定义为最大无符号整数)。这样做的最佳方式是什么?
背景:我需要为linux编写一个模拟几何序列的模块,从中读取将给出下一个元素(模UINT_MAX),我发现的唯一解决方案是将当前元素添加到自身的时间,而使用以下逻辑完成添加:(我用于算术序列)
for(int i=0; i<b; ++i){
if(UINT_MAX - current_value > difference) {
current_value += difference;
} else {
current_value = difference - (UINT_MAX - current_value);
}
当第一次迭代中current_value = a时(并且在每次迭代中更新,并且差异= a(总是)。 显然这不是一个智能的解决方案。 聪明人如何实现这一目标?
谢谢!
答案 0 :(得分:2)
正如已经提到的,如果你的宽度是可用宽度的两倍,那就在这里使用
(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.
当然,如果您需要的是实际还原模UINT_MAX + 1
,正如@Toad所假设的那样,那就是unsigned int
的乘法。
答案 1 :(得分:0)
编辑:正如评论中所指出的......这个答案适用于Modulo MAX_INT + 1 我会把它留在这里,以备将来参考。
这比那更简单:
只需将两个无符号整数相乘,结果也将是无符号整数。 所有不符合unsigned int的东西基本上都不存在。 所以不需要进行模运算:
#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);
}
答案是:0x20000000(所以应该是0x120000000,但答案已被截断,只是您想要的模运算)