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

jwb*_*ley 17 c++ math

我将在C++中这样做,但我不得不用几种语言来做,这是一个相当普遍和简单的问题,这是最后一次.我已经足够像我一样对它进行编码了,我确信必须有一个更好的方法,所以在我用另一种语言写出相同的长卷曲方法之前我在这里发帖;

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

// 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;
}
Run Code Online (Sandbox Code Playgroud)

这可能听起来很小,但这对我来说似乎很重要,只是为了得到两个数字之间的区别.这实际上是一种完全合理的做事方式,我是不必要的迂腐,还是我的狡猾感觉有充分的理由刺痛?

jua*_*nza 43

只需获得差异的绝对值:

#include <cstdlib>
int diff = std::abs(x-y);
Run Code Online (Sandbox Code Playgroud)

  • 只是不要使用`float`或`double` (2认同)
  • @Inverse:`std :: abs`与浮点数和双精度数完美匹配,你只需要包含`<cmath>`.在C中它不会(参数被转换为int - 可能生成编译器警告). (2认同)
  • 这不是容易受到`-` 操作溢出的影响吗?以及 std::abs(INT_MIN) 的未定义行为? (2认同)
  • @craq:确实;考虑 x=INT_MIN+100,y=INT_MAX-100。从根本上说,两个 `int` 值之间的差异是一个 `unsigned int` 数量。 (2认同)

Shi*_*zou 23

std::abs()正如其他人在此提出的那样,使用该功能是一种明确的方法.

但也许你有兴趣在没有库调用的情况下简洁地编写这个函数.

在这种情况下

diff = x > y ? x - y : y - x;
Run Code Online (Sandbox Code Playgroud)

是一个简短的方法.

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

  • 我为什么要眯着眼睛看看你是否已经点缀了你的所有点缀,当我能读到`std :: abs()`并确定代码是什么以及它是无错误的时候?你的`x> y?x - y:y - x`比`std :: abs()`更难阅读,这完全是胡说八道. (8认同)
  • @ShinTakezou重新发明轮子的问题,即使是在这个微不足道的情况下,也是需要别人阅读代码才能弄清楚你的意图是什么.在那一行中有一些'x`和'y'太多,与`std :: abs(xy)相比,你需要花费一两秒的时间来弄清楚你要做什么. (7认同)
  • @ShinTakezou所以,你说重新发明轮子是可以接受的,即使现有的轮子工作得很好? (6认同)
  • @ShinTakezou当您编写与解决当前问题的现有代码完全相同的代码时,您将重新发明轮子.我们批评你答案的原因是它可能会让别人认为这是一种可以接受的做法.另外,这个问题要求_shortest way_:你的方式比标准友好方式长得多,所以它甚至都不是一个好的答案. (3认同)
  • @EtiennedeMartel这是一个有趣的问题.正确性和愚蠢不是完全"分离"的领域.而且,你错过了"背景",这也许是"愚蠢"的原因.我写的其他评论中已经有了严肃的实际观点.(嘿,当然我不是故意粗鲁,我不认为他们以一般的方式存在愚蠢的人;但是,错过了一个背景,证明无法看到一个人所说的话) (2认同)
  • 我没有看到这个答案的问题.std :: abs()显然在实践中几乎总是更好(我认为没有人在争论),但是给出有效的替代品并没有错.最初的问题要求获得两个数字之间差异的"最短路径",而Shin的答案比使用cstdlib要短.顺便说一句,(x> y?x - y:y - x)编译速度比使用abs()快.当然,它的微小差别在大多数情况下并不重要,但有(理论上)理由为什么有人可能更喜欢这个答案. (2认同)
  • @Kev我认为让人们阅读是有用的.它不是严格关于谁是错的/正确的,而是关于*为什么*这个ans在其背景,序言和有用性方面被认为是"错误的"(注意引用).Inn未来读者可能感兴趣的东西?有噪音,真的,但内部噪音有争论,或多或少.我们都应该在几条评论中总结我们的观点??? ---关于*社区维基*答案你删除而不等我回来(至少在删除前24小时?)...这是因为它不适合评论,它也与Q相关.(关键字:演绎). (2认同)

小智 13

#include <cstdlib>

int main()
{
    int x = 7;
    int y = 3;
    int diff = std::abs(x-y);
}
Run Code Online (Sandbox Code Playgroud)

  • 我的"利他主义"徽章在哪里? (4认同)
  • 是的,我不确定这里发生了什么,但我猜它是为了更好? (2认同)
  • @sbi - gotcha.然后,我们应该为rubenvb的利他主义鼓掌,并展示我们网站的独特性,并展示如何为每个人的利益改善答案.很抱歉这两个回滚大家,我自动认为这里发生了一些不好的事情:) (2认同)
  • @rubenvb很好.但这对我来说是非常可耻的 - 也许我应该删除这个问题,你应该添加自己的答案以及这100个声誉转移给你. (2认同)
  • 四个编辑,没有人修复丢失的分号. (2认同)

Max*_*ugh 5

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

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

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

我看到 3 个解决方案。我在这里使用了明确大小的整数类型stdint.h,以消除诸如long和是否int具有相同大小和范围之类的不确定性。

解决方案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;
}
Run Code Online (Sandbox Code Playgroud)

我尝试了一种使用来自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;
}
Run Code Online (Sandbox Code Playgroud)

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

uint32_t difference_int32(int32_t i, int32_t j) {
    return (uint32_t)std::abs((int64_t)i - (int64_t)j);
}
Run Code Online (Sandbox Code Playgroud)

解决方案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.
            // This will never overflow.
            ret = magnitude_of_lesser + magnitude_of_greater;
        }
    }
    return ret;
}
Run Code Online (Sandbox Code Playgroud)