简体   繁体   English

正整数乘以负值

[英]Positive integers that multiply to a negative value

I am learning C++ by reading Stroustrup's "Principles and Practice Using C++". 我正在通过阅读Stroustrup的“使用C ++的原理和实践”来学习C ++。

In the section about pre- and post-conditions there is the following example of function: 在关于前置和后置条件的部分中,有以下功能示例:

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;
}

What confused me is the task about this code: 困扰我的是关于这段代码的任务:

Find a pair of values so that the pre-condition of this version of area holds, but the post-condition doesn't. 找到一对值,以便此版本区域的前置条件成立,但后置条件不会。

Are there such possible values for integer that pre-conditions is ok but post-condition not? 是否存在整数的可能值,前置条件是可以的但后置条件不是?

Are there such possible values for integer that pre-conditions is ok but post-condition not? 是否存在整数的可能值,前置条件是可以的但后置条件不是?

Yes there's a number of input values, that can cause the post condition to fail. 是的,有许多输入值,可能导致后置条件失败。 If eg 如果是

int a = length*width;

overflows the positive int range ( std::numeric_limits<int>::max() ) and the compiler implementation yields a negative value for this case. 溢出正int范围( std::numeric_limits<int>::max() ),编译器实现为这种情况产生负值。


As others noted in their answers, the situation that length*width goes out of bounds from ]0-std::numeric_limits<int>::max()[ is actually undefined behavior, and the post condition renders merely useless, because any value might need to be expected for a . 正如其他人在他们的答案中所指出的那样, length*width超出范围的情况]0-std::numeric_limits<int>::max()[实际上是未定义的行为,并且后置条件呈现仅仅无用,因为任何值可能需要预期a

The key point to fix this, is given in @Deduplicator 's answer , the pre-condition needs to be improved. 解决这个问题的关键点在@Deduplicator回答中给出 ,前提条件需要改进。


As a lance for Bjarne Stroustrup's reasonings to give that example: 作为Bjarne Stroustrup给出这个例子的理由的长矛:

I assume he wanted to point out that such undefined behavior might lead to unexpected negative values in the post-condition and surprising results for a naive assumption checked with the pre-condition. 我假设他想指出这种未定义的行为可能会导致后置条件中出现意外的负值,并且对于使用前置条件检查的天真假设会产生令人惊讶的结果。

No, there aren't any values that will, within the bounds of defined behavior of standard C++, violate the post-condition. 不,在标准C ++的已定义行为的范围内,没有任何值会违反后置条件。 However, there are values that still can make the function behave incorrectly, namely values so large that their product does not fit within an integer. 但是,有些值仍然可以使函数表现不正确,即值太大而其产品不适合整数。 Try passing 200'000 and 15'000. 尝试通过200'000和15'000。

Due to the way most compilers implement C++, you might see the post-condition being violated, but what you're actually observing is undefined behavior due to integer overflow. 由于大多数编译器实现C ++的方式,您可能会看到后置条件被违反,但您实际观察到的是由于整数溢出而导致的未定义行为。

The answer is that his precondition-check is incomplete. 答案是他的前提条件检查不完整。 Even though it is too restrictive. 即使它太严格了。
He failed to include a check that the product can be represented instead of resulting in UB: 他未能检查产品是否可以代表而不是产生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;
}

What comes to my mind is a signed overflow. 我想到的是签名溢出。 It is undefined behavior but might yield a negative value. 它是未定义的行为,但可能会产生负值。
Try std::numeric_limits<int>::max() and 2 . 尝试std::numeric_limits<int>::max()2

Yes if suppose you are using 16 bit computer so int = 2B Max value +32767 so in following 是的,如果假设您使用的是16位计算机,那么int = 2B最大值+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;
}

now final value will be a = -17233 because it gets into -ve value. 现在最终值将是a = -17233因为它进入-ve值。 so second condition gets false. 所以第二个条件变得错误。

Its all depends on range. 一切都取决于范围。

INT_MAX will fail to fulfill the post-condition when used for both length and width for all conforming compilers. 当用于所有符合编译器的长度和宽度时, INT_MAX将无法满足后置条件。

One might be tempted to say that, since the standard guarantees that INT_MAX >=32767, then INT_MAX*INT_MAX will always be greater than INT_MAX and thus not representable in an int which is defined as being able to hold a maximun value of INT_MAX . 有人可能会说,由于标准保证INT_MAX > = 32767,因此INT_MAX*INT_MAX将始终大于INT_MAX ,因此无法在int中表示,该int被定义为能够保持最大值INT_MAX
It is a good argument and it is actually what happens most often, you will get an overflow with most compilers. 这是一个很好的论据,实际上最常发生的事情是,大多数编译器都会出现溢出。

But to cover all bases we need to be aware that the C++ standard states : 但为了涵盖所有基础,我们需要注意C ++标准规定:

3.4.3 3.4.3
1 undefined behavior 1未定义的行为
behavior,upon use of a nonportable or erroneous program construct or of erroneous data,for which this International Standard imposes no requirements 使用不可移植或错误的程序结构或错误数据时的行为,本国际标准不对此要求

2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). 2注意可能的未定义行为包括完全忽略不可预测结果的情况,以及在环境特征(有或没有发出诊断消息)的文档化方式下执行转换或程序执行,终止翻译或执行(使用发出诊断信息)。

3 EXAMPLE An example of undefined behavior is the behavior on integer overflow. 3示例未定义行为的示例是整数溢出的行为。

So it is a bit more serious than not getting the right value for the area. 所以它比没有为该地区获得正确的价值更严重。 When INT_MAX is used for both length and width (or any other combination with a result which is not representable) there is no guarantee of what the compiled program will do. INT_MAX用于长度和宽度(或任何其他具有无法表示的结果的组合)时,无法保证编译程序将执行的操作。 Anything can happen; 任何事情都可能发生; from the likely ones like overflows or crashes to the unlikely ones like disk formats. 从可能的溢出或崩溃到不太可能的,如磁盘格式。

Multiplication of values that overflow the bit representation of value type is undefined because the number of bits overflowed could be more than 1. Thus you could end up with a positive or negative sign bit and the number of lost bits is variable. 溢出值类型的位表示的值的乘法是未定义的,因为溢出的位数可能大于1.因此,您可能最终得到正或负符号位,并且丢失位的数量是可变的。

Example 1: INT_MAX * 2 : result is correct but since the high bit represents the sign bit it is not corrected represented for its type. 示例1: INT_MAX * 2 :结果正确但由于高位表示符号位,因此未对其类型进行校正。

Example 2: INT_MAX * 4 : 1 bit is lost to overflow and the the sign bit is incorrect like in the previous example. 示例2: INT_MAX * 4 :溢出丢失1位,符号位不正确,如上例所示。

Example 3: (INT_MAX + 1) * 2 = 0 : due to overflow of all set bits but sign is correct. 例3: (INT_MAX + 1) * 2 = 0 :由于所有设置位溢出但符号正确。

I am using a 8 bit binary representation to make it easier to read, to show why this happens. 我使用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

In this case there is both soft overflow, no lost information but the representation is incorrect. 在这种情况下,软溢出,没有丢失信息,但表示不正确。 And hard overflow where the bit is no longer present in the result. 并且在结果中不再存在该位的硬溢出。

The difference in the overflows is how the overflow can be detected. 溢出的不同之处在于如何检测溢出。 Typically hard overflows will be detected by the hardware and require very little work for the software to handle. 通常硬件将检测到硬溢出,并且需要很少的工作来使软件处理。 However software overflows may require the software to explicitly test for the overflow condition because the hardware typically does not recognize a sign bit in integer math operations. 但是,软件溢出可能需要软件明确测试溢出条件,因为硬件通常不会识别整数数学运算中的符号位。

How the run-time library handles the overflow is up to the library. 运行时库如何处理溢出取决于库。 Most will ignore it because it is faster to do so, while others may throw an error. 大多数人会忽略它,因为这样做会更快,而其他人可能会抛出错误。 Undefined behavior does not mean it might format your disk. 未定义的行为并不意味着它可能会格式化您的磁盘。 The result of a math operation does not alter the flow of code in any way except as the logic of the code dictates. 除了代码的逻辑指示之外,数学运算的结果不会以任何方式改变代码流。 It can ignore the overflow or try to handle it in some way. 它可以忽略溢出或尝试以某种方式处理它。 The standard does not dictate what method to employ if the code or the hardware tries to handle the problem. 如果代码或硬件试图处理问题,该标准没有规定采用什么方法。

Basically three there are 3 possible things that can happen. 基本上有三种可能发生的事情。
1. The overflow is ignore and the returned value in invalid. 1.溢出被忽略,返回的值无效。
2. The overflow is ignored by the run-time library but the hardware throws an error that is also ignored, resulting in a hard failure of the running code. 2.运行时库忽略溢出但硬件抛出的错误也被忽略,导致运行代码出现硬故障。 In this situation it is completely up to the OS to determine what happens next. 在这种情况下,操作系统完全可以确定接下来会发生什么。 Going nuts and destroying data would to a poor design decision. 疯狂和破坏数据将导致糟糕的设计决策。
3. The overflow is handled by the run-time library which must determine the best way to proceed. 3.溢出由运行时库处理,该库必须确定最佳的继续方式。 Typically this means giving the code a chance to catch the error and handle it, or by shutting down the code as graceful as possible. 通常,这意味着为代码提供捕获错误并处理错误的机会,或者通过尽可能优雅地关闭代码。

Since C++11 there is a boolean value you can test: 从C ++ 11开始,您可以测试一个布尔值:

std::numeric_limits<int>::is_modulo

If this value is true then signed arithmetic behaves in a wraparound fashion, and there is no undefined behaviour in the original code. 如果此值为true则signed signed算法以环绕方式运行,并且原始代码中没有未定义的行为。 A negative value could indeed be produced and so the test in the original code is meaningful. 确实可以产生负值,因此原始代码中的测试是有意义的。

For further discussion of is_modulo see here 有关is_modulo进一步讨论, 请参见此处

So basically, positive values in multiplication ... result in Positive values but these may not actually fit the result type . 所以基本上, 乘法中的正值...导致正值,但这些可能实际上不适合结果类型

Your precondition is not complete, and you postcondition is also invalid. 您的前提条件未完成,您的后置条件也无效。 Not only you can get negative values but also positive values that are just smaller than the input value, all you need is sufficiently large values as input such that the wrap around goes beyond zero, ie a long-wrap-around . 您不仅可以获得负值,而且还可以获得小于输入值的正值,您只需将足够大的值作为输入,使得环绕超过零,即长环绕

You can use this : 你可以用这个

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);
}

to guard against overflow, but then you would want to employ additional checks for FALSE-Positives . 防止溢出,但是你会想要对FALSE-Positives采用额外的检查。

Alternatively if performance is not that much of an issue you can use MPZ library. 或者,如果性能不是很大的问题,您可以使用MPZ库。 If performance is an issue and you want to write assembly for a CPU that has an overflow flag, then you can do just that. 如果性能是一个问题,并且您想为具有溢出标志的CPU编写程序集,那么您可以这样做。 It is possible that your compiler also can do the checks for you eg G++ has fno-strict-overflow or maybe cast to unsigned int after the precondition check. 您的编译器也可以为您执行检查,例如G ++具有fno-strict-overflow或者在前置条件检查后可能转换为unsigned int

At any rate, most of these solutions do not actually solve your problem that results will be foo , that is that you might get smaller area than the actual result. 无论如何,大多数这些解决方案实际上并没有解决你的问题,结果将是foo ,即你可能会得到比实际结果更小的区域。

So your only safe choice is to allow only safe multiplications as shown herein, doing that you miss something, but not that much. 因此,您唯一安全的选择是只允许安全乘法,如此处所示,这样做会错过一些东西,但不会那么多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 按空格分割一串正/负整数 - Split a string of positive/negative integers by space 负整数和正整数之和导致意外输出 - C++ - Sum of negative and positive integers leads to unexpected output - c++ 两个正整数相加得到否定答案。为什么? - Adding two positive integers gives negative answer.Why? 一个读取整数的程序,只要它们交替正负。 C++ - A program that reads integers as long as they are alternately positive and negative. C++ 一种线性时间算法,可重新排列负整数和正整数,中间有零 - A linear-time algorithm that rearrange negative and positive integers with zeroes in between 正负数&amp;&amp;负数负数对正数 - Positive to negative && Negative to positive in negative base C++ 初学者 - 如何仅对用户输入的正整数或负整数求和以及如何计算平均值 - Beginner to C++ - How to sum up only positive or only negative integers the user inputs and how to calculate the avg C++,计算正负数并计算数字的平均值)编写一个程序,读取未指定数量的整数 - C++ ,Count positive and negative numbers and compute the average of numbers) Write a program that reads an unspecified number of integers 在 C++ 中从二进制文件读取字节时,整数有时会偏离正数或负数 256 - Integers are sometimes off by positive or negative 256 when reading bytes from a binary file in C++ 如果 x 为正,我如何将 x 设置为负值,如果 x 为正,我如何将其设置为负。 c++ - How do i set x to be a negative value if x is positive, and if x is positive how do i set it to be negative. c++
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM