Tuesday, 21 February 2012

The Ripped abs Function in C++

I wanted to use the abs function in C++. I checked documentation and found a lack of specifications and seeming contradictions. There were also various versions of functions to get an absolute value (abs, labs, fabs). Instead of trying to make sense of what reference was correct or outdated, I plunged ahead with code to see what would work or what didn’t work. Here’s the results.

Note: I used Visual C++ 2010.
Double Note: The MS documentation on this issue was weak.

Attempt No. 1.

My first attempt was simply to use the abs function on a double variable.

double dVal = -10;
dVal = abs(dVal);

It resulted in a compile error: C3861: 'abs': identifier not found

I knew I needed to include a header file, but I tried it this way to see what would happen.

Attempt No. 2.

What if I added a reference to the standard namespace when calling the abs function?

double dVal = -10;
dVal = std::abs(dVal);

No improvement. This time I got two compiler errors:

C3861: 'abs': identifier not found
C2039: 'abs' : is not a member of 'std'

Attempt No. 3.

Right, time to add a header file, but which one?

#include <cstdlib>
#include <stdlib.h>
#include <cmath>
#include <math.h>.

I saw documentation, text and sample code using one or the other. Instead of continued frustration looking for the answer, I decided I would try each library separately with this code.

#include <cstdlib> | <cmath>
...
double dVal = -10;
dVal = abs(dVal);

The standard library <cstdlib> resulted in a compile error:

C2668: 'abs' : ambiguous call to overloaded function

Running the code with either math library worked fine. The variable dVal becomes 10.000000000000000.

Why the compile error and what header file to include? Part of the confusion lies in the fact the abs function shows up in two different header files.

Here’s what I learnt from the ISO C++ Standard documentation (N3337 2012-01-16). It’s known as C++11—the latest standard of C++, and I should have looked here first.

<math.h> and <stdlib.h> are Standard C library headers. That's the C language, not C++, but since the C++ is a superset of C, the C++ Standard library headers <cmath> <cstdlib> are the same as in C except C++ extends them. (I refer you to paragraph 4 of subclause 26.8 of the ISO C++ Standard.)

The abs function in <cstdlib> takes an integer and returns an integer. If you use the <cmath> library, the function takes a float-point number and returns the same. Both have variations depending on the number of bits used for a particular type.

The <cstdlib> header supports:

int abs (int);
long abs (long);
long long abs (long long);

The long long data type is a 64-bit integer and new to the C++11 standard. This implementation exists in Visual C++ 2010. Microsoft also has it’s own integer types of __intN where N is 8, 16, 32, 64 or 128. __int64 is the same as long long.

The <cmath> header supports:

float abs (float);
double abs (double);
long double abs (long double);


Attempt No. 4.

Focusing on the standard library <cstdlib>, I tested the following code.

#include <cstdlib>
...
int iVal = -10;
iVal = abs(iVal);

The code executes as expected and iVal becomes 10.

But here’s a twist.

#include <cstdlib>
...
iVal = -2147483648;
iVal = abs(iVal);

The value of iVal remains the same. It’s still the exact same negative number. How can that be? To get the answer you have to understand the range of the integer data type. On my computer, int variables are signed 32-bit numbers. The range is -2,147,483,648 to 2,147,483,647. The negative number exceeds the maximum positive number. The abs function worked. It took the negative number, made it positive but since it was larger than the upper limit, it became a negative number. You’ll get the same result if you took the upper limit and added one.


Attempt No. 5.

Focusing on the math library <cmath>, I tested the following code.

#include <cmath>
...
float fVal = -10.123456; // Works.
fVal = abs(fVal);

double dVal = -10.123456; // Works.
dVal = abs(dVal);

The data type long double is the same as double in Visual C++ 2010.

There was no problem using the function.

Attempt No. 6.

The previous code had either the standard library or the math library but not both. What happens when both headers are included? I ran code similar to above and it worked. No compiler errors. No unexpected return values.

Final Thought.

In C, the program has more than one function to determine the absolute value of a number.

abs                 for integers
fabs              for float, double or long double
labs              for long int

Why have variations? Because overloading a function does not happen in C, but it is part of C++. What does that mean? With overloading, the parameters passed to a function can varying depending on their type. In turn, the return value reflects the parameters passed. In C, it needed three different functions to handle the different data types. Not so in C++, yet these C functions  remain because the philosophy of the C++ Standard is to take C and add on to it.

No comments:

Post a Comment