使用 32 位块对 1024 位 2 的补码进行有符号乘法

问题描述

所以我的 1024 位数字有以下结构定义(我想在这里使用 2 的补码表示,并且我在 32 位系统上):

typedef struct int1024
{
    int32_t num[32]; //should I use uint32_t?
} int1024;

基本上是一个保存数字段的数组。

对于加法,有符号加法和无符号加法是一样的。我可以简单地使用指令 addadc 为我更大的数组执行扩展操作。

现在是乘法。我能够使用imul(使用上限和下限结果)和adc的组合使用经典的O(n^2)乘法算法来获得无符号乘法。但是,我的结果需要有符号乘法。我知道人们说只使用带符号的幅度版本。即在负数时取 2 的补码,最后根据需要应用 2 的补码。但是所有额外的注释和加 1 将非常昂贵,因为我需要做很多乘法。有没有一种技术可以对这样的数字表示进行大的有符号乘法。 (我真的不想在这里进行任何分支或递归调用)。我想我的主要问题之一是如何处理进位和负数乘法的高部分。 (只是一个想法,也许我可以使用有符号和无符号乘法的组合,但我不知道从哪里开始)。如果您没有时间处理 1024 位,那么 128 位的答案就足够了(我只是概括一下)。

解决方法

请注意,在 1024 位整数中最高位,即位 1023,是符号位,其位置值为 -(2**1023)。所有其他位都有其正常的 +(2**pos) 位值。即,当您对它们进行加宽乘法时,下“肢”都需要被视为无符号。

您有 1 个符号位和 1023 个低位。每个肢体没有一个符号位。

此外,有符号乘法和无符号乘法之间的区别仅在于扩大乘法的高半部分(N x N => 2N 位)。这就是 x86 为 imul r/m64mul r/m64 对 RDX:RAX 进行完全乘法的单独指令的原因(https://www.felixcloutier.com/x86/imulhttps://www.felixcloutier.com/x86/mul)。但是对于非扩展,只有 imul r,r/mimul r,r/m,imm 编译器用于无符号和有符号(人类也应该如此)。

既然你想要一个 1024x1024 => 1024 位的产品,它会丢弃高 1024 位,你可以而且应该让你的整个东西无符号。

当你用 asm 编写时,你可以使用任何你可以使用的块大小,例如64 位模式下的 64 位,不受 C 块大小的限制。

有关如何使用 https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-large-integer-arithmetic-paper.pdf 的信息,请参阅 BMI2 mulx(保持 FLAGS 不变,因此不会干扰 adc 链,并且只有 RDX 作为隐式输入,其他源和两个输出都是显式,节省 MOV 指令)。还可以选择 Broadwell ADOX / ADCX 并行运行两个 dep 链,以便为中等大小的 BigInteger 内容(例如 512x512 位或 512x64 位(他们用作示例)获得更多 ILP)。