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.
microcontroller - Implementing an absolute value function in C - Electrical Engineering Stack Exchange
absolute difference between numbers - Connect IQ App Development Discussion - Connect IQ - Garmin Forums
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.comHow to efficiently compute absolute value of float in #![no_std]?
Videos
The standard C library is providing the optimized solutions for many problems with considerations based on the architecture, compiler in use and others. The abs() function defined in stdlib.h is one of these, and it is used for your purpose exactly. To emphasize the point, here is ARM compiler result when using abs vs a version of a homebrew abs: https://arm.godbolt.org/z/aO7t1n
Paste:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
srand(111);
int x = rand() - 200;
printf("%d\n", abs(x));
}
results in
main:
push {r4, lr}
mov r0, #111
bl srand
bl rand
sub r1, r0, #200
cmp r1, #0
rsblt r1, r1, #0
ldr r0, .L4
bl printf
mov r0, #0
pop {r4, pc}
.L4:
.word .LC0
.LC0:
.ascii "%d\012\000"
And
#include <stdio.h>
#include <stdlib.h>
int my_abs(int x)
{
return x < 0 ? -x : x;
}
int main(void)
{
srand(111);
int x = rand() - 200;
printf("%d\n", my_abs(x));
}
results in
my_abs:
cmp r0, #0
rsblt r0, r0, #0
bx lr
main:
push {r4, lr}
mov r0, #111
bl srand
bl rand
sub r1, r0, #200
cmp r1, #0
rsblt r1, r1, #0
ldr r0, .L5
bl printf
mov r0, #0
pop {r4, pc}
.L5:
.word .LC0
.LC0:
.ascii "%d\012\000"
Notice that the main code is identical (only a label name is different) in both programs as my_abs got inlined, and its implementation is the same as the standard one.
The speed of a given solution will depend greatly on the architecture, but in C I would say
return (n > 0 ? n : -n);
and let the compiler figure out the best solution.
EDIT: @jonk points out correctly that this will fail for the most-negative possible value of n, assuming that two's-complement arithmetic is used.
Yes, my solution has a conditional branch, but yours has an arithmetic operator and two bitwise operators. Can your microcontroller shift 15 places in a single clock?
We have a little project where we are supposed to program absolute value without math.abs method. The question goes like this: "Suppose x and y are variables of type double. Write a program that reads in x and then sets y to the absolute value of x without calling the Math.abs() method."
Can someone help me with the code? What do you even code to accomplish this? Thank you in advance!!
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.
You could just see if the number is less than 0 and then multiply it by -1