Become a subscriber (Free)

Join 29,000 other subscribers to receive subscriber sale discounts and other free resources.
:
:
Don't worry -- youre-mail address is totally secure. I promise to use it only to send you MicroZine.

Arduino Absolute Value


The Arduino absolute value function finds the magnitude of its argument.

It really just returns positive values from any input value (either negative or positive). Mathematically it is:

    x = |x|

if x =  300 then  |300| = 300, or  abs(300) == 300
if x = -300 then |-300| = 300,
or abs(-300) == 300

That's all fine and simple but the devil is in the details!

Usage Example

An example would be measuring the speed of a robot. Assuming that one of the wheels has a directional encoder attached e.g. a rotating optically encoded disc that returns positive values for forward motion and negative values for backward motion.

You don't want negative speed only positive speed values or the absolute value.

    speed = abs(measured_encoder_delta_change_over_time);

How is abs() defined?

The Arduino absolute value macro is:

    #define abs(x) ((x)>0?(x):-(x))

This will work on any kind of type.

In C code the absolute value operation is a function.

    int abs( int x );

In fact in C the abs function is only defined to work on integer values.

Warning: Arduino defines abs() differently to the C environment.

In C++ there are usually overloads of abs to deal with floats and other types (check documentation).

Note: Other functions: fabs, labs and llabs for float, long, and long long.

What's the problem?

The problem with macros is that they expand code and will not work for all cases. They are fine for single element variables or constant values but problems can occur if you place a multi-operation in the argument of a macro.

Here's the abs() macro working fine

Here's an illustration for the Arduino absolute value MACRO working correctly:

1.1. Arduino absolute value of a constant:

    x = abs(-5);

    Result x is 5.

1.2. Arduino absolute value of an integer variable:

    int a = -5;

    x = abs(a);

    Result x is 5.

1.3. Arduino absolute value of a float variable:

    float fv = -45.22

    x = abs(fv);

    Result x is 45.22.

Here's the abs() macro failing

Here's where the Arduino absolute value MACRO fails:

2.1. Extra operation within macro

    a = -5;

    x = abs(a++);

        Result x is 4    <<<< ERROR

    Serial.print("a after");Serial.println(a);

        Result x is -3    <<<< ERROR

In fact the result you expect to get is 5 and the value of 'a' after the "function call" should -4 but instead it is -3.

The code seems to show the right action but when you write a "normal" C code the post increment operator is supposed to take effect at the end of an operation, not at the beginning. Here it has executed ++ at the beginning. The reason is that the macro is expanded into different C code than you get when you call a function - no function is called.

Lets compare that MACRO abs() to the function version of abs. Here I've re-created it as Iabs():

Function definition Iabs

    int Iabs(int x) { return x<0 ? -x : x; }

2.2. Extra operation within abs function

This example uses a multi-operation in the argument. Here it is post incrementing the argument variable.

    a = -5;

    x = Iabs(a++);

        Result x is 5    <<<< CORRECT

    Serial.println(a);

        Result a is -4    <<<< CORRECT

To understand why this happens you need to expand the macro:

The macro is:
    #define abs(x) ((x)>0?(x):-(x))

    Expand it as the compiler does

    a = -5;

    x = abs(a++); is replaced by:

        ((a++)>0?(a++):-(a++))

On two occasions the variable 'a' can be incremented:

  1. Once in the condition (leftmost parenthesis).
  2. Once in one of the selected results (one of the rightmost parenthesis).
Warning: Use the abs macro only for single variables, no operations.

The same problem will occur if you place a function into the argument of the abs() macro. It will be called twice!

Problems with abs()

Problem with Arduino absolute value macro

Anytime you place anything other than a single constant or variable into the Arduino absolute value macro it will go wrong giving unexpected results (see above).

This means code written for Arduino C/C++ may not work in other compilers because you might take the absolute value of a floating point type in a .ino file but that would cause an error in another compiler because the standard version is only written for integers.

Also, as shown above, the Arduino absolute value macro executes the argument twice causing more serious problems - because they occur at run time (the complier will not complain for you!).

Problems with Arduino absolute value function

Even though the function version of abs works better by not repeatedly executing the argument there is one other problem with it. That is when you use integers and integer mathematics it will return the wrong value. This could also be different on different compilers (depending on the implementation).

This is due to the two's complement method of creating positive and negative numbers.

When you use an extreme value (INT16_MIN defined in inttypes.h) it will return the wrong thing:

Consider

    a = -32768;

    x = Iabs(a)
       
        Result x is  -32768  <<<< ERROR

TIP: A similar problem is in labs() as two's complement is also used.

This GNU documentation indicates that when INT_MIN is used as an input to Iabs() then the output is undefined.

You can either live with it and avoid extreme values or detect the minimum value and output the maximum:

int _abs(int x) {    
     if (x == INT16_MIN)
        return INT16_MAX;
     else
        return x<0 ? -x : x;
}

Now

    a = -32768;

    x = _abs(a)
       
        Result x is  32767  <<<< NEARLY CORRECT

However because of the way two's complement works, the corrected Arduino absolute value of INT16_MIN (-32768) will return 32767. But at least it is not wildly the opposite of what you want i.e. it is only 1 value out.

Different Arduino implementations

It looks like the Arduino Mega, ESP8266 and ESP32 implementation use the standard implementation using a function and not a macro so be aware that there is a difference! It is described as not working with float so the function is int only for these chips!

Source: https://forum.arduino.cc/index.php?topic=626297.0

Using abs in other compilers

It is part of the standard c library and in 'normal' compilers you have to add the include directive to enable its use:

    #include <stdlib.h>

The Arduino environment makes it easier by scanning for known functions in your code and automatically including the relevant header file (so you don't need the line above).

When dealing with microcontrollers used at different companies they will often use a chip that has its own compiler so you will need that #include otherwise the compiler will complain. Because it is such a commonly used function it is defined in stdlib.h - the standard library header file.

Arduino absolute value test sketch

The following code example goes through the various abs options and shows you the problems as described above.

int Iabs(int x) { return x<0 ? -x : x; }

int _abs(int x) {
     if (x == INT16_MIN)
        return INT16_MAX;
     else
        return x<0 ? -x : x;
}

void setup() {
   Serial.begin(115200);
   Serial.println("abs() test");

   Serial.println("\nTest P 1.1 abs macro of a constant. value -5 ");
   Serial.print("abs(-5) = "); Serial.println( abs(-5)  );

   Serial.print("\nTest P 1.2. abs macro of an integer. a = ");
   int a = -5;
   Serial.println( a );
   Serial.print("abs(a) = ");  Serial.println( abs(a)   );

   Serial.print("\nTest P 1.3. abs macro  of a float. fv = ");
   float fv = -45.22;
   Serial.println( fv );
   Serial.print("abs(fv) = ");  Serial.println( abs(fv)   );

   Serial.print("\nTest F 2.1. abs macro extra operation (FAIL). a = ");
   a = -5;
   Serial.println( a );
   Serial.print("abs(a++) = ");Serial.println( abs(a++) );
   Serial.print("a after = ");Serial.println(a);

   Serial.print("\nTest F 2.2. abs fn extra operation (PASS). a = ");
   a = -5;
   Serial.println( a );
   Serial.print("Iabs(a++) = ");Serial.println( Iabs(a++) );
   Serial.print("a after = ");Serial.println(a);

   Serial.print("\nTest F 4.1. does fabs work? fv= ");
   fv = -45.22;
   Serial.println( fv );
   Serial.print("fabs(fv) = ");Serial.println( fabs(fv) );

   Serial.print("\nTest F 4.2. does labs work? fv= ");
   long lv = -32123213123L;
   Serial.println( lv );
   Serial.print("labs(lv) = ");Serial.println( labs(lv) );

/////////////////////////////// LONG
   Serial.print("\n==================== LONG");

   Serial.println("\nTest F 4.0. 32 bit min max ");
   Serial.println( INT32_MIN );
   Serial.println( INT32_MAX );

   Serial.print("\nTest F 4.3. extreme values a =(INT32_MIN+1) = ");
   Serial.println(INT32_MIN+1);
   lv = INT32_MIN+1;
   Serial.print("labs(a) = ");Serial.println( labs(lv) );

   Serial.print("\nTest F 4.4. extreme values INT32_MIN = ");
   Serial.println(INT32_MIN);
   lv = INT32_MIN;
   Serial.print("labs(a) = ");Serial.println( labs(lv) );

///////////////////////////// INT

   Serial.println("\n==================== INT");

   Serial.print("\nTest F 4.0. 16 bit min max ");
   Serial.println( INT16_MIN );
   Serial.println( INT16_MAX );

   Serial.print("\nTest F 3.1. extreme values a =(INT16_MIN+1) = ");
   Serial.println(INT16_MIN+1);
   a = INT16_MIN+1;
   Serial.print("Iabs(a) = ");Serial.println( Iabs(a) );

   Serial.print("\nTest F 3.2. extreme values INT16_MIN = ");
   Serial.println(INT16_MIN);
   a = INT16_MIN;
   Serial.print("Iabs(a) = ");Serial.println( Iabs(a) );

   ///////////////////////////// new abs

   Serial.println("\n==================== new abs");

   Serial.print("\nTest F 5.1. extreme values INT16_MIN = ");
   Serial.println(INT16_MIN);
   a = INT16_MIN;
   Serial.print("_abs(a) = ");Serial.println( _abs(a) );

}

void loop() {
  // put your main code here, to run repeatedly:

}

Sketch output

The output from this sketch on an Arduino Uno is:

abs() test

Test P 1.1 abs macro of a constant. value -5
abs(-5) = 5

Test P 1.2. abs macro of an integer. a = -5
abs(a) = 5

Test P 1.3. abs macro  of a float. fv = -5.22
abs(fv) = 45.⸮abs() test

Test P 1.1 abs macro of a constant. value -5
abs(-5) = 5

Test P 1.2. abs macro of an integer. a = -5
abs(a) = 5

Test P 1.3. abs macro  of a float. fv = -45.22
abs(fv) = 45.22

Test F 2.1. abs macro extra operation (FAIL). a = -5
abs(a++) = 4
a after = -3

Test F 2.2. abs fn extra operation (PASS). a = -5
Iabs(a++) = 5
a after = -4

Test F 4.1. does fabs work? fv= -45.22
fabs(fv) = 45.22

Test F 4.2. does labs work? fv= -2058442051
labs(lv) = 2058442051

==================== LONG
Test F 4.0. 32 bit min max
-2147483648
2147483647

Test F 4.3. extreme values a =(INT32_MIN+1) = -2147483647
labs(a) = 2147483647

Test F 4.4. extreme values INT32_MIN = -2147483648
labs(a) = -2147483648

==================== INT

Test F 4.0. 16 bit min max -32768
32767

Test F 3.1. extreme values a =(INT16_MIN+1) = -32767
Iabs(a) = 32767

Test F 3.2. extreme values INT16_MIN = -32768
Iabs(a) = -32768

==================== new abs

Test F 5.1. extreme values INT16_MIN = -32768
_abs(a) = 32767

Conclusions

The Arduino version of the Arduino absolute value abs() is a macro:

Advantages

  • Fast (no overhead of a function call).
  • Can accept a value of any type (and returns the same type).

Disadvantages

  • Will go wrong if multi-operational or functions are used.
  • Is inconsistent with standard C programming.



New! Comments

Have your say about what you just read! Leave me a comment in the box below.




Privacy Policy | Contact | About Me

Site Map | Terms of Use



Visit our Facebook Page:
To Visit Click Here


Recent Articles

  1. Digispark Attiny85 Easy IDE install and setup

    Digispark ATtiny85: Essential information on setting up and using this tiny, but powerful, chip

    Read more

  2. Magnetometer tilt compensation

    How to make magnetometer tilt compensation work for a magnetometer such as a QMC5883L using an ADXl345 accelerometer.

    Read more

  3. How the HMC5883L 3-axis digital magentomter works

    HMC5883L - How make a digital compass, Find out the differences between the HMC5883L and the QMC5883L and whether they are compatible.

    Read more

  4. Arduino absolute value - abs() - secrets revealed...

    Secrets of Arduino absolute value abs() - Why it might fail, Why it may cause problems...and how to fix it.

    Read more

  5. Arduino Analog Output...Easy analog output generation

    Arduino Analog Output: How to create the most accurate PWM analog ouput and how to create analog PWM sine waves.

    Read more

  6. ACS758 secrets...

    Using the ACS758 to measure very high current

    Read more

Readers Comments

"I wanted to thank
you so so so much
for all the information
you have provided in
your site it's

SUPERB and FANTASTIC."

- Ranish Pottath

"This site really is
the best and my favorite.
I find here many useful
projects and tips."

- Milan

bursach<at>gmail.com<

"Awesome site,
very, very easy and nice
to navigate!"


- Matt
matt_tr<at>
wolf359.cjb.net


Learn Microcontrollers

"Interested in
Microcontrollers?"

Sign up for The
Free 7 day guide:

FREE GUIDE : CLICK HERE


"I am a newbie to PIC
and I wanted to say
 how great your
site has been for me."


- Dave

de_scott<at>bellsouth.net

"Your site is a great
and perfect work.
congratulations."


- Suresh

integratredinfosys<at>
yahoo.com

"I couldn't find the correct
words to define
yourweb site.

Very useful, uncovered,
honest and clear.

Thanks so much for
your time and works.
Regards."


- Anon

Back to Top