C++ 计算两个数字之间差异的最短方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10589559/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:15:20  来源:igfitidea点击:

Shortest way to calculate difference between two numbers?

c++math

提问by jwbensley

I'm about to do this in C++ but I have had to do it in several languages, it's a fairly common and simple problem, and this is the last time. I've had enough of coding it as I do, I'm sure there must be a better method, so I'm posting here before I write out the same long winded method in yet another language;

我即将用 C++ 来做这件事,但我不得不用几种语言来做,这是一个相当普遍和简单的问题,这是最后一次。我已经受够了像我一样编写代码,我确信一定有更好的方法,所以在我用另一种语言写出同样冗长的方法之前,我先在这里发帖;

Consider the (lilies!) following code;

考虑(百合花!)下面的代码;

// I want the difference between these two values as a positive integer
int x = 7
int y = 3
int diff;
// This means you have to find the largest number first 
// before making the subtract, to keep the answer positive
if (x>y) { 
     diff = (x-y);
} else if (y>x) {
     diff = (y-x);
} else if (x==y) {
    diff = 0;
}

This may sound petty but that seems like a lot to me, just to get the difference between two numbers. Is this in fact a completely reasonable way of doing things and I'm being unnecessarily pedantic, or is my spidey sense tingling with good reason?

这可能听起来很小,但对我来说似乎很多,只是为了得到两个数字之间的差异。这实际上是一种完全合理的做事方式,而我不必要地迂腐,还是我的蜘蛛侠感觉有充分的理由?

回答by juanchopanza

Just get the absolute value of the difference:

只需得到差值的绝对值:

#include <cstdlib>
int diff = std::abs(x-y);

回答by ShinTakezou

Using the std::abs()function is one clear way to do this, as others here have suggested.

std::abs()正如这里的其他人所建议的那样,使用该函数是一种明确的方法。

But perhaps you are interested in succinctly writing this function without library calls.

但也许您对简洁地编写这个没有库调用的函数感兴趣。

In that case

在这种情况下

diff = x > y ? x - y : y - x;

is a short way.

是一条捷径。

In your comments, you suggested that you are interested in speed. In that case, you may be interested in ways of performing this operation that do not require branching. This linkdescribes some.

在您的评论中,您建议您对速度感兴趣。在这种情况下,您可能会对不需要分支的执行此操作的方式感兴趣。这个链接描述了一些。

回答by jwbensley

#include <cstdlib>

int main()
{
    int x = 7;
    int y = 3;
    int diff = std::abs(x-y);
}

回答by Rapnar

Well it depends on what you mean by shortest. The fastet runtime, the fastest compilation, the least amount of lines, the least amount of memory. I'll assume you mean runtime.

好吧,这取决于您所说的最短是什么意思。fastet 运行时,最快的编译,最少的行数,最少的内存。我假设你的意思是运行时。

#include <algorithm>    // std::max/min   
int diff = std::max(x,y)-std::min(x,y);

This does two comparisons and one operation (this one is unavoidable but could be optimized through certain bitwise operations with specific cases, compiler might actually do this for you though). Also if the compiler is smart enough it could do only one comparison and save the result for the other comparison. E.g if X>Y then you know from the first comparison that Y < X but I'm not sure if compilers take advantage of this.

这会进行两次比较和一次操作(这是不可避免的,但可以通过特定情况下的某些按位运算进行优化,不过编译器实际上可能会为您执行此操作)。此外,如果编译器足够聪明,它只能进行一个比较并将结果保存为另一个比较。例如,如果 X>Y 那么你从第一次比较中就知道 Y < X 但我不确定编译器是否利用了这一点。

回答by Max Barraclough

All the existing answers will overflow on extreme inputs, giving undefined behaviour. @craq pointed this out in a comment.

所有现有的答案都会在极端输入上溢出,给出未定义的行为。@craq 在评论中指出了这一点。

If you know that your values will fall within a narrow range, it may be fine to do as the other answers suggest, but to handle extreme inputs (i.e. to robustly handle anypossible input values), you cannot simply subtract the values then apply the std::absfunction. As craq rightly pointed out, the subtraction may overflow, causing undefined behaviour (consider INT_MIN - 1), and the std::abscall may also cause undefined behaviour (consider std::abs(INT_MIN)). It's no better to determine the min and max of the pair and to then perform the subtraction.

如果您知道您的值将落在一个狭窄的范围内,那么按照其他答案的建议进行操作可能没问题,但是要处理极端输入(即稳健地处理任何可能的输入值),您不能简单地减去这些值然后应用std::abs功能。正如 craq 正确指出的那样,减法可能会溢出,导致未定义行为(考虑INT_MIN - 1),并且std::abs调用也可能导致未定义行为(考虑std::abs(INT_MIN))。确定该对的最小值和最大值然后执行减法并不是更好。

More generally, a signed intis unable to represent the maximum difference between two signed intvalues. The unsigned inttype should be used for the output value.

更一般地,asigned int无法表示两个signed int值之间的最大差异。该unsigned int类型应该用于输出值。

I see 3 solutions. I've used the explicitly-sized integer types from stdint.hhere, to close the door on uncertainties like whether longand intare the same size and range.

我看到 3 个解决方案。我从使用的明确大小的整数类型stdint.h在这里,收于像是否不确定性的门long,并int有相同的大小和范围。

Solution 1.The low-level way.

解决方案 1.低级方式。

// I'm unsure if it matters whether our target platform uses 2's complement,
// due to the way signed-to-unsigned conversions are defined in C and C++:
// >  the value is converted by repeatedly adding or subtracting
// >  one more than the maximum value that can be represented
// >  in the new type until the value is in the range of the new type
uint32_t difference_int32(int32_t i, int32_t j) {
    static_assert(
        (-(int64_t)INT32_MIN) == (int64_t)INT32_MAX + 1,
        "Unexpected numerical limits. This code assumes two's complement."
    );

    // Map the signed values across to the number-line of uint32_t.
    // Preserves the greater-than relation, such that an input of INT32_MIN
    // is mapped to 0, and an input of 0 is mapped to near the middle
    // of the uint32_t number-line.
    // Leverages the wrap-around behaviour of unsigned integer types.

    // It would be more intuitive to set the offset to (uint32_t)(-1 * INT32_MIN)
    // but that multiplication overflows the signed integer type,
    // causing undefined behaviour. We get the right effect subtracting from zero.
    const uint32_t offset = (uint32_t)0 - (uint32_t)(INT32_MIN);
    const uint32_t i_u = (uint32_t)i + offset;
    const uint32_t j_u = (uint32_t)j + offset;

    const uint32_t ret = (i_u > j_u) ? (i_u - j_u) : (j_u - i_u);
    return ret;
}

I tried a variation on this using bit-twiddling cleverness taken from https://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMaxbut modern code-generators seem to generate worsecode with this variation. (I've removed the static_assertand the comments.)

我尝试使用从https://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax 中获取的巧妙技巧对此进行了一种变体,但现代代码生成器似乎通过这种变体生成了更糟糕的代码。(我已经删除了static_assert和评论。)

uint32_t difference_int32(int32_t i, int32_t j) {
    const uint32_t offset = (uint32_t)0 - (uint32_t)(INT32_MIN);
    const uint32_t i_u = (uint32_t)i + offset;
    const uint32_t j_u = (uint32_t)j + offset;

    // Surprisingly it helps code-gen in MSVC 2019 to manually factor-out
    // the common subexpression. (Even with optimisation /O2)
    const uint32_t t = (i_u ^ j_u) & -(i_u < j_u);
    const uint32_t min = j_u ^ t; // min(i_u, j_u)
    const uint32_t max = i_u ^ t; // max(i_u, j_u)
    const uint32_t ret = max - min;
    return ret;
}

Solution 2.The easy way. Avoid overflow by doing the work using a wider signed integer type. This approach can't be used if the input signed integer type is the largest signed integer type available.

解决方案 2.简单的方法。通过使用更宽的有符号整数类型来避免溢出。如果输入的有符号整数类型是可用的最大有符号整数类型,则不能使用此方法。

uint32_t difference_int32(int32_t i, int32_t j) {
    return (uint32_t)std::abs((int64_t)i - (int64_t)j);
}

Solution 3.The laborious way. Use flow-control to work through the different cases. Likely to be less efficient.

解决方案 3.费力的方式。使用流控制来处理不同的情况。可能效率较低。

uint32_t difference_int32(int32_t i, int32_t j)
{   // This static assert should pass even on 1's complement.
    // It's just about impossible that int32_t could ever be capable of representing
    // *more* values than can uint32_t.
    // Recall that in 2's complement it's the same number, but in 1's complement,
    // uint32_t can represent one more value than can int32_t.
    static_assert( // Must use int64_t to subtract negative number from INT32_MAX
        ((int64_t)INT32_MAX - (int64_t)INT32_MIN) <= (int64_t)UINT32_MAX,
        "Unexpected numerical limits. Unable to represent greatest possible difference."
    );

    uint32_t ret;
    if (i == j) {
        ret = 0;
    } else {

        if (j > i) { // Swap them so that i > j
            const int32_t i_orig = i;
            i = j;
            j = i_orig;
        } // We may now safely assume i > j

        uint32_t magnitude_of_greater; // The magnitude, i.e. abs()
        bool     greater_is_negative; // Zero is of course non-negative
        uint32_t magnitude_of_lesser;
        bool     lesser_is_negative;

        if (i >= 0) {
            magnitude_of_greater = i;
            greater_is_negative = false;
        } else { // Here we know 'lesser' is also negative, but we'll keep it simple
            // magnitude_of_greater = -i; // DANGEROUS, overflows if i == INT32_MIN.
            magnitude_of_greater = (uint32_t)0 - (uint32_t)i;
            greater_is_negative = true;
        }

        if (j >= 0) {
            magnitude_of_lesser = j;
            lesser_is_negative = false;
        } else {
            // magnitude_of_lesser = -j; // DANGEROUS, overflows if i == INT32_MIN.
            magnitude_of_lesser = (uint32_t)0 - (uint32_t)j;
            lesser_is_negative = true;
        }

        // Finally compute the difference between lesser and greater
        if (!greater_is_negative && !lesser_is_negative) {
            ret = magnitude_of_greater - magnitude_of_lesser;
        } else if (greater_is_negative && lesser_is_negative) {
            ret = magnitude_of_lesser - magnitude_of_greater;
        } else { // One negative, one non-negative. Difference is sum of the magnitudes.
            // Looks dangerous but will never overflow.
            ret = magnitude_of_lesser + magnitude_of_greater;
        }
    }
    return ret;
}