可以多倍符号乘法与IMUL指令执行?(can multiprecision signed multi

2019-10-19 03:41发布

我写一个函数库提供所有传统的运算符和函数的签署整数类型s0128s0256s0512s1024和浮点类型f0128f0256f0512f1024

我写了s0128s0256s0512s1024乘套路了,但我得到的是迷惑我错误的结果。 我以为我可以级联与64位乘法imul rcx指令(即产生128位结果在rdx:rax )以同样的方式,我可以做到与无符号运算数相同mul rcx指令......但答案与imul是错误的。

我怀疑有一些技巧,使这项工作,也许混合imulmul指令-什么的。 或者是有一些原因,一个不能执行已签署的乘法指令较大的乘?


所以,你懂技术,我将描述最小的版本, s0128操作数。

           arg2.1   arg2.0  : two 64-bit parts of s0128 operand
           arg1.1   arg1.0  : two 64-bit parts of s0128 operand
           ---------------
       0  out.edx  out.eax  : output of arg1.0 * arg2.0
 out.edx  out.eax           : output of arg1.0 * arg2.1
 -------------------------
 out.2    out.1    out.0    : sum the above intermediate results
 out.edx  out.eax           : output of arg1.1 * arg2.0
 -------------------------
 out.2    out.1    out.0    : sum the above intermediate results

每当码相乘两个64位的值,它生成一个128位结果edx:eax 。 各码生成一个128位的结果时,它总结该结果到一个累积三重与64位寄存器addqadcqadcq指令(其中最终adcq指令只增加零,以保证任何进位标志被传播)。

当我通过小的正数乘以小负数作为试验,结果是否定的,但也有在128位上的64位值中的底部一个或两个非零位s0128结果。 这意味着我的东西是不完全正确,在多倍签署成倍繁殖。

当然级联是更广泛的相当多的s0256s0512s1024

我在想什么? 我必须两个操作数转换为无符号,进行无符号乘法,然后否定的结果,如果一个操作数(但不能同时)为阴性? 或者,我可以计算多倍结果与imul签署乘法指令?

Answer 1:

当你建立多重退出小成倍的扩展精度,你结束了符号和无符号运算的混合物。

特别是,如果你在半打破符号值,你当作签署的上半部,而下半部为无符号。 这同样适用于扩展精度此外真实的,其实。

考虑该任意例如,在AHAL表示的高和低半A ,以及BHBL表示的高和低半B 。 (注:这并不意味着代表的x86寄存器半,只是减半被乘数的。)的L条款是无符号的, H术语签署。

              AH : AL
           x  BH : BL
  -------------------
              AL * BL    unsigned x unsigned => zero extend to full precision
         AH * BL           signed x unsigned => sign extend to full precision
         AL * BH         unsigned x   signed => sign extend to full precision
    AH * BH                signed x   signed

AL * BL产品是无符号的,因为这两个AL和BL是无符号。 因此,它得到当你将其提升到结果的全精度零扩展。

AL * BHAH * BL产品组合符号和无符号值。 得到的产品签名,并需要被符号扩展,当你推动它的结果的全精度。

以下C代码演示了一个32×32的乘法中的16×16乘法方面实现。 相同的原理构建的128×128乘出的64×64乘法运算时适用。

#include <stdint.h>
#include <stdio.h>

int64_t mul32x32( int32_t x, int32_t y )
{
    int16_t x_hi = 0xFFFF & (x >> 16);
    int16_t y_hi = 0xFFFF & (y >> 16);

    uint16_t x_lo = x & 0xFFFF;
    uint16_t y_lo = y & 0xFFFF;


    uint32_t lo_lo = (uint32_t)x_lo * y_lo;    // unsigned x unsigned
    int32_t  lo_hi = (x_lo * (int32_t)y_hi);   // unsigned x   signed
    int32_t  hi_lo = ((int32_t)x_hi * y_lo);   //   signed x unsigned
    int32_t  hi_hi = ((int32_t)x_hi * y_hi);   //   signed x   signed


    int64_t  prod = lo_lo 
                  + (((int64_t)lo_hi + hi_lo) << 16) 
                  + ((int64_t)hi_hi << 32);

    return prod;
}

int check(int a, int b)
{
    int64_t ref = (int64_t)a * (int64_t)b;
    int64_t tst = mul32x32(a, b);

    if (ref != tst)
    {
        printf("%.8X x %.8X => %.16llX vs %.16llX\n",
                (unsigned int)a,         (unsigned int)b, 
                (unsigned long long)ref, (unsigned long long)tst);
        return 1;
    }

    return 0;
}


int main()
{
    int a = (int)0xABCDEF01;
    int b = (int)0x12345678;
    int c = (int)0x1234EF01;
    int d = (int)0xABCD5678;

    int fail = 0;

    fail += check(a, a);
    fail += check(a, b);
    fail += check(a, c);
    fail += check(a, d);

    fail += check(b, b);
    fail += check(b, c);
    fail += check(b, d);

    fail += check(c, c);
    fail += check(c, d);

    fail += check(d, d);

    printf("%d tests failed\n", fail);
    return 0;
}

这种模式,即使你打破被乘数成两片以上延伸。 也就是说,只有最显著件有符号数为签署得到处理。 所有其他片是无符号。 考虑下面这个例子,其中将每个被乘数为3个部分:

                      A2 : A1 : A0
                   x  B2 : B1 : B0
  ---------------------------------
                           A0 * B0    => unsigned x unsigned   => zero extend
                      A1 * B0         => unsigned x unsigned   => zero extend
                 A2 * B0              =>   signed x unsigned   => sign extend
                      A0 * B1         => unsigned x unsigned   => zero extend
                 A1 * B1              => unsigned x unsigned   => zero extend
            A2 * B1                   =>   signed x unsigned   => sign extend
                 A0 * B2              => unsigned x   signed   => sign extend
            A1 * B2                   => unsigned x   signed   => sign extend
       A2 * B2                        =>   signed x   signed

因为所有的混合符号性和符号扩展的乐趣,它往往只是更容易实现签约×符号乘法作为一个无符号×无符号乘法,并在年底有条件地否定,如果迹象,如果被乘数不同。 (而且,事实上,当你到扩展精度浮点数,只要你留在符号 - 幅度格式如IEEE-754,你不会有处理多重签名。)

该组件宝石显示如何有效地否定扩展精度值。 (该宝石页面是有点过时,但你会发现它很有趣/有用)。



文章来源: can multiprecision signed multiply be performed with imul instruction?