在another question中,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 ++规范说整数溢出是Undefined Behavior;允许编译器假设你不这样做;因此x+1
的论点不能是INT_MAX
;因此编译器可能(并将会)编译test
函数以无条件地返回true
。到目前为止,非常好。
然而,C++11 spec也说(18.3.2.4第60-61段):
static constexpr is_modulo;
如果类型是模数,则为真.222对于任何操作,类型是模数 将
+
,-
或*
与该类型的值相关联 超出范围[min(),max()]
,返回的值不同 从真实值乘以max() - min() + 1
的整数倍。在大多数计算机上,对于浮动类型,
false
为true
无符号整数,true
用于有符号整数。
请注意,第5节第(4)段仍然写着:“如果在评估表达式期间,结果未在数学上定义或未在其类型的可表示值范围内,则行为未定义。”没有提到is_modulo == true
创建例外。
所以我认为标准在逻辑上是矛盾的,因为整数溢出不能同时定义和未定义。或者至少,GCC是不符合要求的,因为它is_modulo
为true
,即使签名算术肯定不会回滚。
是标准车吗?海湾合作委员会不符合规定吗?我错过了什么吗?
答案 0 :(得分:18)
如果is_modulo
为true
的有符号类型(例如int
),通常的算术转换没有改变,那么除了除以零之外的任何算术运算都有一个模数映射到类型范围内的单个值的(数学)整数中的正确结果,因此实现被约束为表现为好像实际结果是以模型范围为模的真实结果。因此,如果实现希望将溢出算法保留为未定义,则必须将is_modulo
设置为false
。
在gcc邮件列表中讨论了 ad nauseam ,然后在PR 22200下进行了讨论,最终确定is_modulo
的值应为false
类型; the change是在今年4月份对libstdc ++进行的。
请注意,在C ++ 03中,语言明显不同:
18.2.1.2 numeric_limits members [lib.numeric.limits.members]56 - [...]如果可以添加两个正数并且具有结果,则类型是模数 包裹到第三个更少的数字。
鉴于未定义的行为任何是可能的,可以说libstdc ++的先前行为(is_modulo
为true
)是正确的并且与行为相符克++;关于相关PR的早期讨论应该记住这一点。