如何在不使用long long
的情况下将64位无符号整数与32位无符号整数相乘。 64位数字存储为两个32位数字的数组。
换句话说,我如何从e
,f
和a
获得b
和c
给出以下内容:
( a * 2^32 + b ) * c = e * 2^32 + f
a
,b
,c
,e
和f
都是32位无符号整数,只有32位数学运算可用。
答案 0 :(得分:3)
首先要意识到的是,我们将获得一个96位数字,如下所示:
(2 64 - 1)×(2 32 - 1)
= 2 64 ×2 32 - 2 64 - 2 32 + 1
= 2 96 - 2 64 - 2 32 + 1
< 2 96
接下来要认识到的是我们不能使用32位乘法。即使没有携带,结果也太大了。
(2 32 - 1)×(2 32 - 1)
= 2 32 ×2 32 - 2 32 - 2 32 + 1
= 2 64 - 2 33 + 1
≥2 32
我们需要执行16位乘法运算。即使使用16位进位,也可以使用32位数进行16位乘法,而不会发生溢出。
FFFF 16 ×FFFF 16 + FFFF 16
= FFFF_0000 16
< 2 32
如果我们将数字视为16位值,则乘法如下:
+-----+-----+-----+-----+
| ahi | alo | bhi | blo |
+-----+-----+-----+-----+
+-----+-----+
× | chi | clo |
+-----+-----+
===========================================
+-----+-----+-----+-----+-----+-----+
| dhi | dlo | ehi | elo | fhi | flo |
+-----+-----+-----+-----+-----+-----+
我们都知道如何从小学进行上述乘法,对吗?
+-----+-----+-----+-----+ +-----+
| ahi | alo | bhi | blo | × | clo |
+-----+-----+-----+-----+ +-----+
+-----+-----+-----+-----+-----+ +-----+
+ | ahi | alo | bhi | blo | 0 | × | chi |
+-----+-----+-----+-----+-----+ +-----+
以下是我们需要的所有工具:
void mul16c(
uint16_t i, uint16_t j, uint16_t cin,
uint16_t *prod_ptr, uint16_t *cout_ptr
) {
uint32_t prod = i * j + cin;
*prod_ptr = prod & 0xFFFF:
*cout_ptr = prod >> 16;
}
void add16c(
uint16_t i, uint16_t j, uint16_t cin,
uint16_t *sum_ptr, uint16_t* cout_ptr
) {
uint32_t sum = i + j + cin;
*sum_ptr = sum & 0xFFFF:
*cout_ptr = sum >> 16;
}
我们可以按如下方式使用它们:
void mul64x32(
uint32_t i[2], uint32_t j,
uint32_t (*prod_ptr)[2], uint32_t *cout_ptr
) {
uint16_t i16[4];
i16[3] = i[1] >> 16;
i16[2] = i[1] & 0xFFFF;
i16[1] = i[0] >> 16;
i16[0] = i[0] & 0xFFFF;
uint16_t j16[2];
j16[1] = j[0] >> 16;
j16[0] = j[0] & 0xFFFF;
uint16_t p[6];
{
uint16_t carry = 0;
mul16c(i16[0], j16[0], carry, &(p[0]), &carry);
mul16c(i16[1], j16[0], carry, &(p[1]), &carry);
mul16c(i16[2], j16[0], carry, &(p[2]), &carry);
mul16c(i16[3], j16[0], carry, &(p[3]), &carry);
p[4] = carry;
p[5] = 0;
}
uint16_t q[6];
{
uint16_t carry = 0;
q[0] = 0;
mul16c(i16[0], j16[1], carry, &(q[1]), &carry);
mul16c(i16[1], j16[1], carry, &(q[2]), &carry);
mul16c(i16[2], j16[1], carry, &(q[3]), &carry);
mul16c(i16[3], j16[1], carry, &(q[4]), &carry);
q[5] = carry;
}
uint16_t product[6];
{
uint16_t carry = 0;
add16c(p[0], q[0], carry, &(product[0]), &carry);
add16c(p[1], q[1], carry, &(product[1]), &carry);
add16c(p[2], q[2], carry, &(product[2]), &carry);
add16c(p[3], q[3], carry, &(product[3]), &carry);
add16c(p[4], q[4], carry, &(product[4]), &carry);
add16c(p[5], q[5], carry, &(product[5]), &carry);
}
(*prod_ptr)[0] = ( product[1] << 16 ) | product[0];
(*prod_ptr)[1] = ( product[3] << 16 ) | product[2];
*cout_ptr = ( product[5] << 16 ) | product[4];
}
每个数组的元素零预计是该数字中最不重要的部分。