删除常量表达式中带符号整数溢出的未定义行为?

whn*_*whn 2 c++ integer-overflow undefined-behavior constexpr c++20

编辑在实际示例中,似乎可能会发生负溢出,我还添加了一个示例来演示那里的错误

我正在使用 C++20 并尝试将依赖于 Java 和 C# 中的有符号整数溢出的库转换为 C++ 代码。我还尝试生成它在编译时使用的表,并允许它们在编译时可用。

在我的代码中,我在引用看起来像这样的代码时遇到错误(重现错误的最小示例,该解决方案也将解决我的问题):

#include <iostream> 

constexpr auto foo(){
    std::int64_t a = 2; 
    std::int64_t very_large_constant = 0x598CD327003817B5L; 
    std::int64_t x = a * very_large_constant; 
    return x; 
}
 
int main(){
    std::cout << foo() << std::endl; 
    return 0; 
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/TvM45vd8d

负溢出版本

#include <iostream> 

constexpr auto foo(){
    std::int64_t a = -2; 
    std::int64_t very_large_constant = 0x598CD327003817B5L; 
    std::int64_t x = a * very_large_constant; 
    return x; 
}
 
int main(){
    std::cout << foo() << std::endl; 
    return 0; 
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/7zoE9r18E

我得到 12905529061151879018 超出了分别由 long long 和 -12905529061151879018 表示的范围。

我知道这里不允许未定义的行为,我也认识到 GCC 和 MSVC 不会在这里出错,并且您可以放置​​一个标志来让 clang 编译它。但是我应该怎么做才能真正解决这个问题而不切换编译器或应用标志来忽略无效的 constexpr?

有什么方法可以定义我期望并希望在这里发生的行为吗?

Dre*_*ann 6

有符号整数在您可以命名的任何实现中都具有二进制补码布局。从 C++20 开始,还保证使用二进制补码布局。

这意味着您可以对整数执行数学运算,并获得与您希望整数执行的操作unsigned相匹配的明确定义的溢出行为。signed

#include <iostream> 
#include <bit>

constexpr auto foo(){
    std::uint64_t a = 2; 
    std::uint64_t very_large_constant = 0x598CD327003817B5L; 
    std::uint64_t x = a * very_large_constant; 
    return static_cast<std::int64_t>(x); 
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么需要“bit_cast”?C++20 承诺转换为签名者整数会回绕,因此一个简单的 `static_cast&lt;std::int64_t&gt;` 应该就足够了。 (2认同)

Nic*_*las 5

您不能使用有符号整数来执行此操作。然而,C++20 中有一些值得您信赖的东西:

  1. 无符号整数溢出是明确定义的。

  2. 有符号整数需要表示为 2 的补码。

  3. 相应大小的整数和无符号整数之间的转换保留位模式。

因此,您可以使用显式无符号类型和文字来完成所有基于溢出的数学,然后在需要时将它们转换为有符号值。需要进行此转换以保持位不变。

  • 正如我还对德鲁的回答发表评论一样,“static_cast”应该代替“bit_cast”,除非我真的错过了一些东西。C++20 承诺到有符号整数的转换在算术上以“二进制补码”方式运行,与它实际如何表示为位无关。 (2认同)
  • @Krupip:不,“std::uint64_t a = -2;”可以正常工作,尽管看起来很奇怪。它将把“0xfffffffffffffffe”分配给“a”,它在加法、减法和乘法中起到了“-2”的作用。 (2认同)