Alright, the following works. @user16217248 got me started. See the discussion under 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;
}
The secret is to do the num1 > num2 ternary comparison with signed integer values, but then to reinterpret cast them to unsigned integer values to allow for well-defined overflow and underflow behavior when getting the absolute value of num1 - num2.
Here is my full test code:
absolute_value_of_num1_minus_num2.c from my eRCaGuy_hello_world repo:
///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)
{
// Note: due to implicit type promotion rules, rule 2 in my answer here
// (https://stackoverflow.com/a/72654668/4561887) applies, and both the `>`
// comparison, as well as subtraction, take place below as `int` types.
// While signed `int` overflow and underflow is undefined behavior, none of
// that occurs here.
// - It's just useful to understand that even though we are doing
// `(uint8_t)num1 -(uint8_t)num2`, the C compiler really sees it as this:
// `(int)(uint8_t)num1 - (int)(uint8_t)num2`.
// - This is because all small types smaller than `int`, such as `uint8_t`,
// are first automatically implicitly cast to `int` before any
// mathematical operation or comparison occurs.
// - The C++ compiler therefore sees it as this:
// `static_cast<int>(static_cast<unsigned char>(num1)) - static_cast<int>(static_cast<unsigned char>(num2))`.
// - Run this code through https://cppinsights.io/ to see that.
// See here: https://cppinsights.io/s/bfc425f6 --> and click the play
// button.
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;
}
Sample run and output:
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
Adjacently related
- The "absolute subtraction" above reminds me of the "rounding divide" set of solutions you can do with integer math too. For that, see my other answer here: Rounding integer division (instead of truncating). I present rounding up, rounding down, and rounding to nearest when doing integer division.
See also
- My answer on implicit casting/promotion and Integer and floating point rank and promotion rules in C and C++
- https://cppinsights.io/ - a very useful tool which expands your C++ code into exactly what the compiler sees, including after applying all automatic implicit type promotion rules in the compiler.
- Ex: see my code above here: https://cppinsights.io/s/bfc425f6 --> then click the play button to convert and expand it into what the compiler sees.
Alright, the following works. @user16217248 got me started. See the discussion under 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;
}
The secret is to do the num1 > num2 ternary comparison with signed integer values, but then to reinterpret cast them to unsigned integer values to allow for well-defined overflow and underflow behavior when getting the absolute value of num1 - num2.
Here is my full test code:
absolute_value_of_num1_minus_num2.c from my eRCaGuy_hello_world repo:
///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)
{
// Note: due to implicit type promotion rules, rule 2 in my answer here
// (https://stackoverflow.com/a/72654668/4561887) applies, and both the `>`
// comparison, as well as subtraction, take place below as `int` types.
// While signed `int` overflow and underflow is undefined behavior, none of
// that occurs here.
// - It's just useful to understand that even though we are doing
// `(uint8_t)num1 -(uint8_t)num2`, the C compiler really sees it as this:
// `(int)(uint8_t)num1 - (int)(uint8_t)num2`.
// - This is because all small types smaller than `int`, such as `uint8_t`,
// are first automatically implicitly cast to `int` before any
// mathematical operation or comparison occurs.
// - The C++ compiler therefore sees it as this:
// `static_cast<int>(static_cast<unsigned char>(num1)) - static_cast<int>(static_cast<unsigned char>(num2))`.
// - Run this code through https://cppinsights.io/ to see that.
// See here: https://cppinsights.io/s/bfc425f6 --> and click the play
// button.
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;
}
Sample run and output:
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
Adjacently related
- The "absolute subtraction" above reminds me of the "rounding divide" set of solutions you can do with integer math too. For that, see my other answer here: Rounding integer division (instead of truncating). I present rounding up, rounding down, and rounding to nearest when doing integer division.
See also
- My answer on implicit casting/promotion and Integer and floating point rank and promotion rules in C and C++
- https://cppinsights.io/ - a very useful tool which expands your C++ code into exactly what the compiler sees, including after applying all automatic implicit type promotion rules in the compiler.
- Ex: see my code above here: https://cppinsights.io/s/bfc425f6 --> then click the play button to convert and expand it into what the compiler sees.
A simple solution to this problem is to avoid overflow entirely by always subtracting the smaller one from the bigger one. This gives the expected results, even for x == INT_MIN and y == INT_MAX. The signed to unsigned conversion here is safe:
unsigned diff = x > y ? (unsigned)x-(unsigned)y : (unsigned)y-(unsigned)x;
Edit: In order for the subtraction to be guaranteed to not cause signed overflow in cases of the 'smaller' one being less than zero, the operands must be cast to unsigned.
How to write absolute value in c - Stack Overflow
Absolute Difference? - C++ Forum
Getting wrong absolute value in C++/C - Stack Overflow
Absolute value without calling the Math.abs() method?
I think you can probably figure this one out just with a hint.
What happens to negative numbers when they are multiplied by -1?
Note that you can check if numbers are less than zero using if and then do something about less than zero numbers.
If you still get stuck after thinking on that for a few minutes shoot me a pm.
Edit: as long as you don't ask me to just write out all the code for you.
More on reddit.comVideos
The shortest solution in your first piece of code is to change the printf statement as follows:
printf("absValue = %u\n", (unsigned)((u<0)?-u:u));
This will print the absolute value of u. The type conversion (unsigned) ensures that the data type is as expected by printf. The statement (u<0)?-u:u uses the conditional operator to select the value -u if the condition (u<0) is true and u if the condition is false (i.e. u>=0).
The problem in your code is that u is a signed integer which means its value is stored using the Two's complement representation in 4 bytes(*) and printf is not intelligent. When you tell printf to display an unsigned integer, then printf will take the 4 bytes holding u and interpret them as an unsigned integer. Since negative numbers in Two's complement are stored as large positive integers, that is the result you see.
(*) The use of Two's complement and the int size of 4 is machine-dependent, but common.
As an alternative, you can also use the standard C function abs() (or one of its related functions):
7.22.6.1 The abs, labs and llabs functions
Synopsis
#include <stdlib.h> int abs(int j); long int labs(long int j); long long int llabs(long long int j);Description
The
abs,labs, andllabsfunctions compute the absolute value of an integerj. If the result cannot be represented, the behavior is undefined.Returns
The
abs,labs, andllabs, functions return the absolute value.Footnotes
The absolute value of the most negative number cannot be represented in two's complement.
Note the footnote "The absolute value of the most negative number cannot be represented in two's complement." and "If the result cannot be represented, the behavior is undefined." Strictly speaking, you'd likely need to use long long int and llabs() to avoid undefined behavior in converting INT_MIN to a positive value, assuming a 32-bit int value, and long is often 32-bits, even on 64-bit Windows.
However, since double values are likely implemented in IEEE format with 53 bits of precision, a 32-bit int value can be converted to double with no loss of precision, so you can use the fabs() function to get the absolute value of a 32-bit int value in one call:
7.12.7.2 The fabs functions
Synopsis
#include <math.h> double fabs(double x); float fabsf(float x); long double fabsl(long double x);The
fabsfunctions compute the absolute value of a floating-point numberx.
So your code would be:
#include <stdio.h>
#include <math.h>
int main (int argc, char *argv[]) {
int u;
scanf("%d", &u);
printf("absValue = %u\n", (unsigned) fabs((double) u));
return 0;
}
Note that in (unsigned) fabs((double) u), casting u to double is not strictly necessary, as the int value will be implicitly converted to a double because of the double fabs(double) function prototype from stdlib.h. But the cast back to unsigned is exremely necessary to pass the unsigned int value you want to pass to printf().
You could also do this:
#include <stdio.h>
#include <math.h>
int main (int argc, char *argv[]) {
int u;
scanf("%d", &u);
unsigned int absValue = fabs(u);
printf("absValue = %u\n", absValue);
return 0;
}
That works because unsigned int absValue is explicitly an unsigned int.
Also, on modern CPUs, conversion between int and double is usually done by a single relatively fast instruction.
By subtract unsigned values like this, it might wrap.
If you want to get the absolute value of a unsigned do something like this:
unsigned absoluteValue = (value1>value2)?(value1-value2):(value2-value1);undeflow
First of all in C++ such call is ambiguous. If you compiled the code in C then your result shall be equal to 6 in the both cases. The result you got can be get if you will cast the argument to double. For example
absValue = std::abs( double( value1 -value2 ) );
std::cout << "absoluteValue = " << absoluteValue << ", absValue = " << absValue << std::endl;
Compare two results of the function call in the following code snippet
unsigned value1 = 4;
unsigned value2 = 10;
unsigned absoluteValue = std::abs( int( value1-value2 ) );
int absValue = std::abs( int( value1 -value2 ) );
std::cout << "absoluteValue = " << absoluteValue << ", absValue = " << absValue << std::endl;
std::cout << std::endl;
absoluteValue = std::abs( double( value1-value2 ) );
absValue = std::abs( double( value1 -value2 ) );
std::cout << "absoluteValue = " << absoluteValue << ", absValue = " << absValue << std::endl;
std::cout << std::endl;
The output will be
absoluteValue = 6, absValue = 6
absoluteValue = 4294967290, absValue = -2147483648
Why is there such a difference?
Expression value1 - valu2 has type unsigned int. Here is two-complement arithmetic. Then function abs is applied in the first case the expression is converted to signed int and will be equal to -6 according to the two-complement arithmetic. So function will return 6 for the both variable.
In the second case the (positive due to the type unsigned int) expression is converted to double and the function returns a double. It will be a positive value equal to 4294967290. Now this value for the second variable is converted to signed int and you get -2147483648