是numeric_limits< int> :: is_modulo从逻辑上矛盾

Is numeric_limits<int>::is_modulo logically contradictory?

本文关键字:is modulo 逻辑上 矛盾 int numeric limits lt gt      更新时间:2023-10-16

在另一个问题中,std::numeric_limits<int>::is_modulo的主题出现了。但是我考虑的越多,似乎规格或GCC或两者都出现了问题。

让我从一些代码开始:

#include <limits>
#include <iostream>
bool test(int x)
{
    return x+1 > x;
}
int main(int argc, char *argv[])
{
    int big = std::numeric_limits<int>::max();
    std::cout << std::numeric_limits<int>::is_modulo << " ";
    std::cout << big+1 << " ";
    std::cout << test(big) << "n";
}

当我用g++ -O3 -std=c++11(X86_64 GCC 4.7.2)编译此内容时,它会产生以下输出:

1 -2147483648 1

也就是说, is_modulo是正确的,一个加上 INT_MAX是负的,一个加上 INT_MAX大于 INT_MAX

如果您是那种有任何回答这个问题的现实机会的人,那么您已经知道这里发生了什么。C 规范说整数溢出是不确定的行为。允许编译器假设您不这样做;因此,对x+1的论点不能是INT_MAX;因此,编译器可以(并将)编译test函数以无条件返回true。到目前为止,一切都很好。

但是,C 11规格也说(18.3.2.4段60-61):

static constexpr is_modulo;

true如果类型为模拟。222如果任何操作,则类型为modulo 涉及+-*的该类型的值将其结果的值 落在范围内[min(),max()]之外,值返回的值有所不同 来自 max() - min() + 1的整数倍数的真实值。

在大多数机器上,这是浮动类型的falsetrue用于 未签名的整数和签名整数的true

请注意,第5款(4)仍然读到:"如果在评估表达式期间,该结果在数学上未定义或不在其类型的代表值范围内,则行为是未定义的。"没有提及is_modulo == true创建异常。

因此,在我看来,标准在逻辑上是矛盾的,因为整数溢出不能同时定义和未定义。或至少,GCC不合格,因为它具有is_modulotrue,即使签名算术最肯定不会缠绕。

是标准越野车吗?GCC不合格吗?我想念什么吗?

如果 is_modulo是签名类型的 true(例如 int),而通常由通常的算术转换而没有变化,那么对于零除零以外的任何算术操作,零都有一个正确的结果(数学)整数将模块映射到类型范围内的单个值,因此实现被限制为行为,好像实际结果是类型范围的真实结果。因此,如果实现要保留溢出算术,则必须将is_modulo设置为false

在GCC邮件列表中讨论了 adaauseam ,然后在PR 22200下,最终结论is_modulo的值应为签名类型的CC_28的值;更改是在今年4月对LibstDC 进行的。

请注意,在C 03中,语言有显着不同:

18.2.1.2 Numeric_Limits成员[lib.numeric.limits.members]

56 - [...]如果可以添加两个正数,则类型为模型,并具有结果 缠绕到第三个数字。

鉴于有可能使用不确定的行为是可能的,因此可以说libstdc 的先前行为(具有is_modulo作为true)是正确的,并且与G 的行为一致。应考虑到有关链接的PR的较早讨论。