绝对差值是两个数字之间的差值的绝对值.假设我有2int个变量(xy),我想找出绝对差异.一个简单的解决方案是:

unsigned diff = abs(x-y);

然而,如果发生溢出,则这些调用未定义的行为并给出不正确的结果,例如,如果xINT_MIN,yINT_MAX.这是returns 1(假设回绕行为),而不是预期的UINT_MAX.

推荐答案

好的,以下是可行的方法.@user16217248让我起步.参见that answer岁以下的讨论.

How to safely and efficiently find abs((int)num1 - (int)num2)

/// Safely and efficiently return `abs((int)num1 - (int)num2)`
unsigned int abs_num1_minus_num2_int(int num1, int num2)
{
    unsigned int abs_diff = num1 > num2 ?
        (unsigned int)num1 - (unsigned int)num2 :
        (unsigned int)num2 - (unsigned int)num1;

    return abs_diff;
}

秘诀是对signed个整数值进行num1 > num2个三进制比较,然后将它们重新解释为unsigned个整数值,以便在获得绝对值num1 - num2时允许明确定义的溢出和下溢行为.

以下是我的完整测试代码:

我的eRCaGuy_hello_world份回购中有absolute_value_of_num1_minus_num2.c份:

///usr/bin/env ccache gcc -Wall -Wextra -Werror -O3 -std=gnu17 "$0" -o /tmp/a -lm && /tmp/a "$@"; exit
// For the line just above, see my answer here: https://stackoverflow.com/a/75491834/4561887

#include <limits.h>
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
#include <stdio.h>   // For `printf()`


#define TEST_EQ(func, num1, num2, equals) \
    printf("%s\n", func((num1), (num2)) == (equals) ? "Passed" : "FAILED")

/// Safely and efficiently return `abs((int8_t)num1 - (int8_t)num2)`
uint8_t abs_num1_minus_num2_int8(int8_t num1, int8_t num2)
{
    uint8_t abs_diff = num1 > num2 ?
        (uint8_t)num1 - (uint8_t)num2 :
        (uint8_t)num2 - (uint8_t)num1;

    // debugging
    printf("num1 = %4i (%3u); num2 = %4i (%3u); num1-num2=%3u;  ",
        num1, (uint8_t)num1, num2, (uint8_t)num2, abs_diff);

    return abs_diff;
}

/// Safely and efficiently return `abs((int)num1 - (int)num2)`
unsigned int abs_num1_minus_num2_int(int num1, int num2)
{
    unsigned int abs_diff = num1 > num2 ?
        (unsigned int)num1 - (unsigned int)num2 :
        (unsigned int)num2 - (unsigned int)num1;

    // debugging
    printf("num1 = %11i (%10u); num2 = %11i (%10u); num1-num2=%10u;  ",
        num1, (unsigned int)num1, num2, (unsigned int)num2, abs_diff);

    return abs_diff;
}


int main()
{
    printf("Absolute difference tests.\n");

    // ---------------
    // int8_t types
    // ---------------

    int8_t num1_8;
    int8_t num2_8;

    printf("\n");
    printf("INT8_MIN = %i, INT8_MAX = %i\n", INT8_MIN, INT8_MAX); // -128, 127

    num1_8 = -7;
    num2_8 = -4;
    TEST_EQ(abs_num1_minus_num2_int8, num1_8, num2_8, 3);

    num1_8 = INT8_MIN;
    num2_8 = INT8_MAX;
    TEST_EQ(abs_num1_minus_num2_int8, num1_8, num2_8, UINT8_MAX);

    num1_8 = INT8_MAX;
    num2_8 = INT8_MIN;
    TEST_EQ(abs_num1_minus_num2_int8, num1_8, num2_8, UINT8_MAX);

    num1_8 = 100;
    num2_8 = 10;
    TEST_EQ(abs_num1_minus_num2_int8, num1_8, num2_8, 90);

    num1_8 = 10;
    num2_8 = 100;
    TEST_EQ(abs_num1_minus_num2_int8, num1_8, num2_8, 90);

    num1_8 = 10;
    num2_8 = 10;
    TEST_EQ(abs_num1_minus_num2_int8, num1_8, num2_8, 0);

    num1_8 = INT8_MAX;
    num2_8 = 1;
    TEST_EQ(abs_num1_minus_num2_int8, num1_8, num2_8, 126);

    num1_8 = 1;
    num2_8 = INT8_MAX;
    TEST_EQ(abs_num1_minus_num2_int8, num1_8, num2_8, 126);

    // ---------------
    // int types
    // ---------------

    int num1;
    int num2;

    printf("\n");
    printf("INT_MIN = %i, INT_MAX = %i\n", INT_MIN, INT_MAX); // -2147483648, 2147483647

    num1 = -7;
    num2 = -4;
    TEST_EQ(abs_num1_minus_num2_int, num1, num2, 3);

    num1 = INT_MIN;
    num2 = INT_MAX;
    TEST_EQ(abs_num1_minus_num2_int, num1, num2, UINT_MAX);

    num1 = INT_MAX;
    num2 = INT_MIN;
    TEST_EQ(abs_num1_minus_num2_int, num1, num2, UINT_MAX);

    num1 = 100;
    num2 = 10;
    TEST_EQ(abs_num1_minus_num2_int, num1, num2, 90);

    num1 = 10;
    num2 = 100;
    TEST_EQ(abs_num1_minus_num2_int, num1, num2, 90);

    num1 = 10;
    num2 = 10;
    TEST_EQ(abs_num1_minus_num2_int, num1, num2, 0);

    num1 = INT_MAX;
    num2 = 1;
    TEST_EQ(abs_num1_minus_num2_int, num1, num2, 2147483646);

    num1 = 1;
    num2 = INT_MAX;
    TEST_EQ(abs_num1_minus_num2_int, num1, num2, 2147483646);


    return 0;
}

示例运行和输出:

eRCaGuy_hello_world/c$ ./absolute_value_of_num1_minus_num2.c
Absolute difference tests.

INT8_MIN = -128, INT8_MAX = 127
num1 =   -7 (249); num2 =   -4 (252); num1-num2=  3;  Passed
num1 = -128 (128); num2 =  127 (127); num1-num2=255;  Passed
num1 =  127 (127); num2 = -128 (128); num1-num2=255;  Passed
num1 =  100 (100); num2 =   10 ( 10); num1-num2= 90;  Passed
num1 =   10 ( 10); num2 =  100 (100); num1-num2= 90;  Passed
num1 =   10 ( 10); num2 =   10 ( 10); num1-num2=  0;  Passed
num1 =  127 (127); num2 =    1 (  1); num1-num2=126;  Passed
num1 =    1 (  1); num2 =  127 (127); num1-num2=126;  Passed

INT_MIN = -2147483648, INT_MAX = 2147483647
num1 =          -7 (4294967289); num2 =          -4 (4294967292); num1-num2=         3;  Passed
num1 = -2147483648 (2147483648); num2 =  2147483647 (2147483647); num1-num2=4294967295;  Passed
num1 =  2147483647 (2147483647); num2 = -2147483648 (2147483648); num1-num2=4294967295;  Passed
num1 =         100 (       100); num2 =          10 (        10); num1-num2=        90;  Passed
num1 =          10 (        10); num2 =         100 (       100); num1-num2=        90;  Passed
num1 =          10 (        10); num2 =          10 (        10); num1-num2=         0;  Passed
num1 =  2147483647 (2147483647); num2 =           1 (         1); num1-num2=2147483646;  Passed
num1 =           1 (         1); num2 =  2147483647 (2147483647); num1-num2=2147483646;  Passed

相邻关联

  1. 上面的"绝对减法"让我想起了"四舍五入除法"的一组解决方案,你也可以用整数数学来做.关于这个问题,请看我的另一个答案:Rounding integer division (instead of truncating).在进行整数除法时,我介绍了向上舍入、向下舍入和舍入到最近的整数.

C++相关问答推荐

球体—立方体重叠:无、部分或全部?

C:二进制搜索和二进制插入

减法运算结果的平方的最快方法?

变量>;-1如何在C中准确求值?

GLIBC:如何告诉可执行文件链接到特定版本的GLIBC

为什么GDB/MI进程的FIFO循环中有read()阻塞

函数的限制限定指针参数允许优化调用方函数吗?

OpenSSL:如何将吊销列表与SSL_CTX_LOAD_VERIFY_LOCATIONS一起使用?

如何在GET_STRING输入后对少数几个特定字符串进行C判断?

为 struct 中的数组动态分配内存时出错

处理EPOLL_WAIT中的接收数据和连接关闭信号

这个空指针类型的转换是有效代码还是恶意代码?

如何在双向表中实现线程安全,每个条目仅使用4位,同时避免任何全局锁?

哪些C++功能可以在外部C块中使用

在哪里可以找到叮当返回码的含义?

如何找出C中分配在堆上的数组的大小?

尽管将其标记为易失性,但 gcc 是否优化了我的等待代码?

GDB 跳过动态加载器代码

如何确保 gcc + libc 对于多字节字符串使用 UTF-8,对于 wchar_t 使用 UTF-32?

当 a 是代码块时使用逗号运算符 (a, b)