在 C 中将两个 32 位数字相乘的错误结果
Wrong results multiplying two 32 bit numbers in C
我正在尝试在 C 中对矩阵进行两次乘法运算,但我不明白为什么会得到这些结果...
我想做:Btranspose * B
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#define LOW_WORD(x) (((x) << 16) >> 16)
#define HIGH_WORD(x) ((x) >> 16)
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#define SIGN(x) (((x) >= 0) ? 1 : -1)
#define UNSIGNED_MULT(a, b) \
(((LOW_WORD(a) * LOW_WORD(b)) << 0) + \
(((int64_t)((LOW_WORD((a)) * HIGH_WORD((b))) + (HIGH_WORD((a)) * LOW_WORD((b))))) << 16) + \
((int64_t)(HIGH_WORD((a)) * HIGH_WORD((b))) << 32))
#define MULT(a, b) (UNSIGNED_MULT(ABS((a)), ABS((b))) * SIGN((a)) * SIGN((b)))
int main()
{
int c,d,k;
int64_t multmatrix[3][3];
int64_t sum64 = 0;
int32_t Btranspose[3][3] = {{15643, 24466, 58751},
{54056, 26823, -25563},
{-33591, 54561, -13777}};
int32_t B[3][3] = {{15643, 54056, -33591},
{24466, 26823, 54561},
{58751, -25563, -13777}};
for ( c = 0 ; c < 3 ; c++ ){
for ( d = 0 ; d < 3 ; d++ ){
for ( k = 0 ; k < 3 ; k++ ){
sum64 = sum64 + MULT(Btranspose[c][k], B[k][d]);
printf("\n the MULT for k = %d is: %ld \n", k, MULT(Btranspose[c][k], B[k][d]));
printf("\n the sum for k = %d is: %ld \n", k, sum64);
}
multmatrix[c][d] = sum64;
sum64 = 0;
}
}
printf("\n\n multmatrix \n");
for( c = 0 ; c < 3; c++ ){
printf("\n");
for( d = 0 ; d < 3 ; d++ ){
printf(" %ld ", multmatrix[c][d]);
}
}
return 0;
}
下面的输出是错误的,我注意到错误是在将第 3 个元素 (58751 * 58751) 与 k=2 相乘时出现的。
我认为不会溢出,因为 58751^2 需要 32 位。
k = 0 的 MULT 是:244703449
k = 0 的总和是:244703449
k = 1 的 MULT 是:598585156
k = 1 的总和是:843288605
k = 2 的 MULT 是:46036225 // 这是错误的!!!
k = 2 的总和是:889324830
.
.
.
.
k = 2 的 MULT 是:189805729
k = 2 的总和是:1330739379
多重矩阵
889324830 650114833 324678230
650114833 1504730698 -308929574
324678230 -308929574 1330739379
正确的结果应该是
multmatrix - correct
4.2950e+09 -2.2870e+03 1.2886e+04
-2.2870e+03 4.2950e+09 -1.2394e+05
1.2886e+04 -1.2394e+05 4.2951e+09
为什么矩阵相乘不对??
上面的代码应该怎么改才能让两个矩阵相乘不会溢出??
(我正在尝试编写一个程序,将两个 32 位数字相乘以导入到只有 32 位寄存器的系统中)
所以根据下面的回答,这实际上有效。
#define LOW_WORD(x) ((uint32_t)(x) & 0xffff)
#define HIGH_WORD(x) ((uint32_t)(x) >> 16)
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#define SIGN(x) (((x) >= 0) ? 1 : -1)
#define UNSIGNED_MULT(a, b) \
(((LOW_WORD(a) * LOW_WORD(b)) << 0) + \
((int64_t)(LOW_WORD(a) * HIGH_WORD(b) + HIGH_WORD(a) * LOW_WORD(b)) << 16) + \
((int64_t)(HIGH_WORD((a)) * HIGH_WORD((b))) << 32))
#define MULT(a, b) (UNSIGNED_MULT(ABS((a)), ABS((b))) * SIGN((a)) * SIGN((b)))
感谢您帮助我了解一些事情!我会尝试将整个事情转化为功能并将其发回。
这个
(((x) << 16) >> 16)
不会像您预期的那样生成无符号的 16 位数字。该表达式的类型与x
的类型相同,即int32_t
(有符号整数)。事实上,如果使用任何合理的(二进制补码)C 实现,对于 x=58751
:
x = 00000000000000001110010101111111
(x) << 16 = 11100101011111110000000000000000 (negative number)
(((x) << 16) >> 16) = 11111111111111111110010101111111 (negative number)
要正确提取低 16 位,请使用无符号算法:
((uint32_t)(x) & 0xffff)
或(保持您的风格)
((uint32_t)(x) << 16 >> 16)
要得到高位字,也得用无符号算术:
((uint32_t)(x) >> 16)
此外,编译器可能需要帮助确定此表达式的范围(以进行优化):
(uint16_t)((uint32_t)(x) & 0xffff)
一些(全部?)编译器足够聪明,可以自己完成这项工作。
另外,正如doynax所述,低位字和高位字的乘积是一个32位数字(或31位,但无所谓)。要将其左移 16 位,您必须将其转换为 64 位类型,就像您对高位字所做的那样:
((int64_t)(LOW_WORD(a) * HIGH_WORD(b) + HIGH_WORD(a) * LOW_WORD(b)) << 16)
我正在尝试在 C 中对矩阵进行两次乘法运算,但我不明白为什么会得到这些结果...
我想做:Btranspose * B
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#define LOW_WORD(x) (((x) << 16) >> 16)
#define HIGH_WORD(x) ((x) >> 16)
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#define SIGN(x) (((x) >= 0) ? 1 : -1)
#define UNSIGNED_MULT(a, b) \
(((LOW_WORD(a) * LOW_WORD(b)) << 0) + \
(((int64_t)((LOW_WORD((a)) * HIGH_WORD((b))) + (HIGH_WORD((a)) * LOW_WORD((b))))) << 16) + \
((int64_t)(HIGH_WORD((a)) * HIGH_WORD((b))) << 32))
#define MULT(a, b) (UNSIGNED_MULT(ABS((a)), ABS((b))) * SIGN((a)) * SIGN((b)))
int main()
{
int c,d,k;
int64_t multmatrix[3][3];
int64_t sum64 = 0;
int32_t Btranspose[3][3] = {{15643, 24466, 58751},
{54056, 26823, -25563},
{-33591, 54561, -13777}};
int32_t B[3][3] = {{15643, 54056, -33591},
{24466, 26823, 54561},
{58751, -25563, -13777}};
for ( c = 0 ; c < 3 ; c++ ){
for ( d = 0 ; d < 3 ; d++ ){
for ( k = 0 ; k < 3 ; k++ ){
sum64 = sum64 + MULT(Btranspose[c][k], B[k][d]);
printf("\n the MULT for k = %d is: %ld \n", k, MULT(Btranspose[c][k], B[k][d]));
printf("\n the sum for k = %d is: %ld \n", k, sum64);
}
multmatrix[c][d] = sum64;
sum64 = 0;
}
}
printf("\n\n multmatrix \n");
for( c = 0 ; c < 3; c++ ){
printf("\n");
for( d = 0 ; d < 3 ; d++ ){
printf(" %ld ", multmatrix[c][d]);
}
}
return 0;
}
下面的输出是错误的,我注意到错误是在将第 3 个元素 (58751 * 58751) 与 k=2 相乘时出现的。 我认为不会溢出,因为 58751^2 需要 32 位。
k = 0 的 MULT 是:244703449 k = 0 的总和是:244703449 k = 1 的 MULT 是:598585156 k = 1 的总和是:843288605 k = 2 的 MULT 是:46036225 // 这是错误的!!! k = 2 的总和是:889324830 . . . . k = 2 的 MULT 是:189805729 k = 2 的总和是:1330739379 多重矩阵 889324830 650114833 324678230 650114833 1504730698 -308929574 324678230 -308929574 1330739379
正确的结果应该是
multmatrix - correct
4.2950e+09 -2.2870e+03 1.2886e+04
-2.2870e+03 4.2950e+09 -1.2394e+05
1.2886e+04 -1.2394e+05 4.2951e+09
为什么矩阵相乘不对?? 上面的代码应该怎么改才能让两个矩阵相乘不会溢出??
(我正在尝试编写一个程序,将两个 32 位数字相乘以导入到只有 32 位寄存器的系统中)
所以根据下面的回答,这实际上有效。
#define LOW_WORD(x) ((uint32_t)(x) & 0xffff)
#define HIGH_WORD(x) ((uint32_t)(x) >> 16)
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#define SIGN(x) (((x) >= 0) ? 1 : -1)
#define UNSIGNED_MULT(a, b) \
(((LOW_WORD(a) * LOW_WORD(b)) << 0) + \
((int64_t)(LOW_WORD(a) * HIGH_WORD(b) + HIGH_WORD(a) * LOW_WORD(b)) << 16) + \
((int64_t)(HIGH_WORD((a)) * HIGH_WORD((b))) << 32))
#define MULT(a, b) (UNSIGNED_MULT(ABS((a)), ABS((b))) * SIGN((a)) * SIGN((b)))
感谢您帮助我了解一些事情!我会尝试将整个事情转化为功能并将其发回。
这个
(((x) << 16) >> 16)
不会像您预期的那样生成无符号的 16 位数字。该表达式的类型与x
的类型相同,即int32_t
(有符号整数)。事实上,如果使用任何合理的(二进制补码)C 实现,对于 x=58751
:
x = 00000000000000001110010101111111
(x) << 16 = 11100101011111110000000000000000 (negative number)
(((x) << 16) >> 16) = 11111111111111111110010101111111 (negative number)
要正确提取低 16 位,请使用无符号算法:
((uint32_t)(x) & 0xffff)
或(保持您的风格)
((uint32_t)(x) << 16 >> 16)
要得到高位字,也得用无符号算术:
((uint32_t)(x) >> 16)
此外,编译器可能需要帮助确定此表达式的范围(以进行优化):
(uint16_t)((uint32_t)(x) & 0xffff)
一些(全部?)编译器足够聪明,可以自己完成这项工作。
另外,正如doynax所述,低位字和高位字的乘积是一个32位数字(或31位,但无所谓)。要将其左移 16 位,您必须将其转换为 64 位类型,就像您对高位字所做的那样:
((int64_t)(LOW_WORD(a) * HIGH_WORD(b) + HIGH_WORD(a) * LOW_WORD(b)) << 16)