64位无符号数乘法结果错误:原因排查与修正咨询
尝试用HLA实现两个64位无符号数相乘并将结果存入128位变量,参考相关方法编写代码后,测试小数值(如$84B8_8BF3_1E4E_3B0C)结果正确,但测试最大64位无符号数$FFFFFFFFFFFFFFFF时,输出结果与预期的FFFFFFFFFFFFFFFE0000000000000001不符,第13字节出错。将代码最后一行的adc(0,edx);改为adc(1,edx);后问题解决,现咨询以下问题:
- 这个1的来源是什么?
- 代码中的错误在哪里?
- 如何正确修改程序?
原始代码:
program exercise; static qw1:qword:= 956_3547_6850_2300_5452;//$84B8_8BF3_1E4E_3B0C qw2:qword:= 956_3547_6850_2300_5452;//$84B8_8BF3_1E4E_3B0C //qw1:qword:= 18_446_744_073_709_551_615;//$FFFF_FFFF_FFFF_FFFF //qw2:qword:= 18_446_744_073_709_551_615;//$FFFF_FFFF_FFFF_FFFF prd:lword :=0; #include("Stdlib.hhf"); begin exercise; mov((type dword qw1[0]),eax); mul((type dword qw2[0]),eax); mov(eax,(type dword prd[0])); mov(edx,ecx); mov((type dword qw1[4]),eax); mul((type dword qw2[0]),eax); add(ecx,eax); mov(eax,ebx); adc(0,edx); mov(edx,ecx); mov((type dword qw1[0]),eax); mul((type dword qw2[4]),eax); add(ebx,eax); mov(eax,(type dword prd[4])); adc(ecx,edx); mov(edx,ecx); mov((type dword qw1[4]),eax); mul((type dword qw2[4]),eax); add(ecx,eax); mov(eax,(type dword prd[8])); adc(0,edx); mov(edx,(type dword prd[12])); stdout.put(prd, " ",(type uns128 prd)); end exercise;
解答
1. 这个1的来源是什么?
你改成adc(1,edx)能解决问题是巧合,这个1并非正确逻辑里的固定值。实际正确逻辑中,需要加到edx的是第四步add(ecx,eax)执行后产生的进位标志CF。由于前面步骤的错误,导致ecx值异常,add(ecx,eax)实际产生了CF=1,手动加1刚好抵消了前面的错误,但这不是正确的解决方式。
2. 代码中的错误在哪里?
核心错误出在第三步的进位处理逻辑:
adc(ecx,edx); mov(edx,ecx);
这部分完全颠倒了操作对象。第三步中,add(ebx,eax)会产生进位CF,我们需要把a0*b1的高32位(存在edx)和进位CF一起加到之前累积的进位值ecx中,而不是把ecx加到edx里。这一错误导致ecx存储的累积进位值错误,最终在第四步计算时,add(ecx,eax)的结果异常,进而影响最高32位的输出结果。
另外,第四步的adc(0,edx)本身逻辑是正确的(将add产生的CF加到edx),但因为前面的错误,才需要手动改1来“修复”。
3. 如何正确修改程序?
修正第三步的进位处理逻辑,将错误的:
adc(ecx,edx); mov(edx,ecx);
改为:
adc(edx, ecx);
(HLA中adc(source, dest)表示dest = dest + source + CF,这行代码直接把edx和CF加到ecx中,无需再执行mov(edx,ecx))
完整修正后的代码:
program exercise; static qw1:qword:= 18_446_744_073_709_551_615;//$FFFF_FFFF_FFFF_FFFF qw2:qword:= 18_446_744_073_709_551_615;//$FFFF_FFFF_FFFF_FFFF //qw1:qword:= 956_3547_6850_2300_5452;//$84B8_8BF3_1E4E_3B0C //qw2:qword:= 956_3547_6850_2300_5452;//$84B8_8BF3_1E4E_3B0C prd:lword :=0; #include("Stdlib.hhf"); begin exercise; mov((type dword qw1[0]),eax); mul((type dword qw2[0]),eax); mov(eax,(type dword prd[0])); mov(edx,ecx); mov((type dword qw1[4]),eax); mul((type dword qw2[0]),eax); add(ecx,eax); mov(eax,ebx); adc(0,edx); mov(edx,ecx); mov((type dword qw1[0]),eax); mul((type dword qw2[4]),eax); add(ebx,eax); mov(eax,(type dword prd[4])); adc(edx, ecx); // 修正此处的进位处理逻辑 mov((type dword qw1[4]),eax); mul((type dword qw2[4]),eax); add(ecx,eax); mov(eax,(type dword prd[8])); adc(0,edx); // 保留正确的逻辑 mov(edx,(type dword prd[12])); stdout.put(prd, " ",(type uns128 prd)); end exercise;
修改后,无论是小数值还是最大64位无符号数,计算结果都会与预期一致。
内容的提问来源于stack exchange,提问作者ahs

