如何以标准/便携和高效的方式编写int64 = int32 * int32?

时间:2015-04-11 14:40:53

标签: c++ c

相关: Is this treatment of int64_t a GCC AND Clang bug?

我能想到的唯一解决方案是将其中一个操作数显式转换为int64,强制该产品至少为int64

但如果以这种方式完成,那么它可以根据编译器的智能实际执行int64*int32int64*int64,或理想情况下将其优化回{{1} }}

正如相关问题中所讨论的,将int32*int32的结果分配给int32*int32并不会改变int64已经导致UB的事实。

有什么想法吗?

2 个答案:

答案 0 :(得分:9)

您已经以标准,便携,高效的方式表明了如何做到这一点:

int64_t mul(int32_t x, int32_t y) {
    return (int64_t)x * y;
    // or static_cast<int64_t>(x) * y if you prefer not to use C-style casts
    // or static_cast<int64_t>(x) * static_cast<int64_t>(y) if you don't want
    // the integral promotion to remain implicit
}

您的问题似乎是关于假设的体系结构,其具有与功能签名相对应的汇编指令

int64_t intrinsic_mul(int32_t x, int32_t y);
int64_t intrinsic_mul(int64_t x, int64_t y);
int64_t intrinsic_mul(int64_t x, int32_t y); // and maybe this too

并且,在这个假设的体系结构中,第一个具有相关优势,而此外,编译器在编译上述函数时无法使用此指令,并且最重要的是,它失败了提供对上述内在的访问。

我希望这样的场景真的很少见,但是如果你真的发现自己处于这种情况,大多数编译器也允许你编写内联汇编,这样你就可以编写一个调用它的函数直接使用特殊指令,并且仍然提供足够的元数据,以便优化器可以有效地使用它(例如,使用符号输入和输出寄存器,以便优化器可以使用它想要的任何寄存器,而不是使寄存器选择硬编码。)

答案 1 :(得分:5)

内置算术表达式仅针对同类操作数类型退出。任何涉及混合类型的表达都意味着整数提升,并且算术操作本身只是为同类型定义并应用于同类型。

选择int32_tint64_t

正如您可能正确理解的那样,对于两种类型算术运算(至少+-*)的选择都容易受到UB溢出的影响,但是不会有溢出在两个int64_t上运行时,两者都可以表示为int32_t s。例如,以下工作:

int64_t multiply(int32_t a, int32_t b)
{
    // guaranteed not to overflow, and the result value is equal
    // to the mathematical result of the operation
    return static_cast<int64_t>(a) * static_cast<int64_t>(b);
}

作为示例,以下是GCC如何将其转换为Linux上的x86和x86_64(请注意不同的调用约定):

multiply(int, int):

// x86 (32-bit, "-m32 -march=i386")     x86-64 ("-m64 -march=x86-64")
// args are on the stack                args are in EDI, ESI
// return in EDX:EAX                    return in RAX

mov     eax, DWORD PTR [esp+8]          movsx   rax, edi
                                        movsx   rsi, esi
imul    DWORD PTR [esp+4]               imul    rax, rsi
ret                                     ret