乘为负值的正整数 我通过阅读Stroustrup的“C++原理与实践”来学习C++。

乘为负值的正整数 我通过阅读Stroustrup的“C++原理与实践”来学习C++。,c++,integer,C++,Integer,在关于前置和后置条件的部分中,有以下函数示例: int area(int length, int width) // calculate area of a rectangle; // pre-conditions: length and width are positive // post-condition: returns a positive value that is the area { if (length<=0 || width <=0) e

在关于前置和后置条件的部分中,有以下函数示例:

int area(int length, int width)
// calculate area of a rectangle;
// pre-conditions: length and width are positive
// post-condition: returns a positive value that is the area
{
    if (length<=0 || width <=0) 
        error("area() pre-condition");

    int a = length*width;

    if (a<=0) 
        error("area() post-condition");

    return a;
}
int区域(int长度、int宽度)
//计算矩形的面积;
//前提条件:长度和宽度为正
//post条件:返回一个正的值,该值表示面积
{
如果(长度)
整数是否存在这样的可能值,即前置条件可以,而后置条件不能

是的,有许多输入值,可能导致post条件失败

int a = length*width;
溢出正的
int
范围(
std::numeric\u limits::max()
),编译器实现在这种情况下产生负值


正如其他人在回答中指出的那样,
length*width
超出
]0-std::numeric_limits::max()[
的范围实际上是未定义的行为,post条件仅仅是无用的,因为
a
可能需要任何值

解决这一问题的关键点是在年代,前提条件需要改进


作为比亚恩·斯特劳斯图普给出该示例的理由:


我想他想指出,这种未定义的行为可能会导致post条件中出现意外的负值,并导致使用前置条件检查的天真假设的意外结果。

我想到的是有符号溢出。这是未定义的行为,但可能会产生负值。

尝试
std::numeric\u limits::max()<代码> > 2 > <代码> .< /p> < p>不,没有任何值,在标准C++的定义行为范围内,违反了后条件。但是,仍然有一些函数会使函数行为不正确,即值太大,以致于它们的产品不适合于一个整数。尝试通过200和15。

<> P>由于大多数编译器实现C++,您可能会看到后条件被违反,但是实际上观察到的是由于整数溢出而导致的未定义行为。如果您使用的是16位计算机,那么INT/2P>YES,那么INT= 2B MAX值+ 32767,因此在下面的

{
    length = 500, width = 100;
    if (length<=0 || width <=0) error("area() pre-condition");
    int a = length*width;   // a = 500 * 100 = 50000
    if (a<=0) error("area() post-condition");
    return a;
}
{
长度=500,宽度=100;
如果(长度用于所有符合要求的编译器的长度和宽度,则不能满足post条件

有人可能会说,由于标准保证
INT\u MAX
>=32767,那么
INT\u MAX*INT\u MAX
将始终大于
INT\u MAX
,因此不能表示为
INT
,而
INT
定义为能够保持
INT\u MAX
的最大值
这是一个很好的参数,实际上它是最经常发生的情况,大多数编译器都会出现溢出

但为了涵盖所有基础,我们需要意识到,各州:

3.4.3
1未定义的行为
使用不可移植或错误的程序结构或错误的数据时的行为,本国际标准对此不作要求

2注:可能的未定义行为范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特有的记录方式进行行为 (无论是否发出诊断消息),终止转换或执行(发出诊断消息)

3示例未定义行为的一个示例是整数溢出行为


因此,这比没有获得正确的区域值要严重一些。当长度和宽度都使用
INT_MAX
时(或任何其他组合,其结果无法表示)无法保证编译后的程序会做什么。任何事情都可能发生,从可能发生的溢出或崩溃到不可能发生的磁盘格式。

答案是他的前提条件检查不完整。尽管它限制太多。
他未能包括一项检查,以确保产品能够代表,而不是导致UB:

int area(int length, int width) {
    // calculate area of a rectangle
    assert(length >= 0 && width >= 0 && (!width
        || std::numeric_limits<int>::max() / width >= length));
    int a = length * width;
    assert(a >= 0); // Not strictly neccessary - the math is easy enough
    return a;
}
int区域(int长度、int宽度){
//计算矩形的面积
断言(长度>=0&&width>=0&&width
||std::numeric_limits::max()/width>=length));
int a=长度*宽度;
assert(a>=0);//严格来说这不是必需的-数学很简单
返回a;
}

值类型的位表示溢出的值的乘法未定义,因为溢出的位数可能超过1。因此,您可能会得到正号或负号位,并且丢失的位数是可变的

示例1:
INT_MAX*2
:结果是正确的,但由于高位代表符号位,因此未针对其类型进行更正

示例2:
INT_MAX*4
:1位因溢出而丢失,符号位不正确,如前一示例所示

示例3:
(INT_MAX+1)*2=0
:由于所有设置位溢出,但符号正确

我正在使用8位二进制表示,以使其更易于阅读,从而说明为什么会发生这种情况

0111 1111              // Max positive signed value
+1
1000 0000              // Sign bit set but binary value is correct
*2
0000 0000              // Upper bit is lost due to overflow
在这种情况下,既有软溢出,没有丢失信息,但表示不正确,也有硬溢出,即结果中不再存在位

溢出的不同之处在于如何检测溢出。通常硬件会检测到硬溢出,软件只需很少的工作即可处理。但是,软件溢出可能需要软件显式测试溢出情况,因为硬件通常无法识别中的符号位整数数学运算

运行时库如何处理溢出取决于库。大多数库会忽略它,因为这样做更快,而其他库可能会抛出错误。 未定义的行为不适合我
std::numeric_limits<int>::is_modulo
bool multiplication_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits+b_bits<=32);
}