34

Beware, I am talking about ::abs(), not std::abs()

According to the cplusplus.com website, abs is supposed to behave differently for the stdlib.h C version, if you include <cmath>

Here is an extract from the this page (which deals with ::abs, not std::abs):

double abs (double x); 
float abs (float x); 
long double abs (long double x);
Compute absolute value
/*
Returns the absolute value of x: |x|.
These convenience abs overloads are exclusive of C++. In C, abs is only declared
in  <cstdlib> (and operates on int values). 
The additional overloads are provided in this header (<cmath>) for the integral types: 
These overloads effectively cast x to a double before calculations 
(defined for T being any integral type).
*/

Really???

I have been bitten by this when porting a program to a new platform, since different compilers and standard libraries implementation differ here.

Here is my sample program:

#include <iostream>
//#include <stdlib.h>//Necessary inclusion compil under linux
//You can include either cmath or math.h, the result is the same
//#include <cmath>
#include <math.h>
int main(int argc, const char * argv[])
{
  double x = -1.5;
  double ax = std::abs(x);
  std::cout << "x=" << x << " ax=" << ax << std::endl;
  return 0;
}

And here is the result under MSVC 2010:

  • No compilation warning is emitted under MSVC 2010, and the program will compile even if you do not include neither math.h nor stdlib.h: it seems like math.h and stdlib.h are always included whatever you do
  • The program output is: x=-1.5 ax=1.5 (seemingly correct according to the reference)

Now here is the result under OSX:

  • No compilation warning is emitted, even with the -Wall flag (the double to int cast is not signaled)! The result is the same if you replace g++ by llvm-g++. The inclusion of math.h or cmath is not required for the compilation.
  • The program output is: x=-1.5 ax=1

And finally the result under Linux:

  • The program will not compile if stdlib.h is not included (at last, one compiler that does not include stdlib automatically). No compilation warning is emitted for the double -> int cast.
  • The program output is: x=-1.5 ax=1

No clear winner here. I know that an obvious answer is "prefer std::abs to ::abs", but I wonder:

  • Is the cplusplus.com website right here when it says that abs should automatically provide double version outside of the std namespace?
  • Are all compiler and their standard libraries wrong here except MSVC (although it includes math.h silently)?
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Pascal T.
  • 3,866
  • 4
  • 33
  • 36
  • 12
    `cstdlib` is not a C header, so cplusplus.com is wrong there (surprise surprise). Also note that including `iostream` can confuse things, because it can include `cmath` and/or `cstdlib`, both of which may introduce various `abs` overloads in the global namespace. So get rid of that. – juanchopanza Jan 27 '14 at 22:07
  • Hang on; are we using `std::abs` or just `::abs`? (Using MinGW, the above complains that `'abs' is not a member of 'std'`; changing to `::abs` gives me the 1 result. – tabstop Jan 27 '14 at 22:07
  • 2
    "the double to int cast is not signaled" - there's no double to int cast in your code. "Is cplusplus.com right" - **I don't know,** but cplusplus.com is *notorious* for containing incorrect and/or misleading information, so my guess would be "no, cplusplus.com is wrong". The same applies to MSVC (if 3 compilers agree and MSVC disagrees, then it's very likely that MSVC is wrong, but again, I don't know, I'm not a C++ programmer.) –  Jan 27 '14 at 22:07
  • 2
    cplusplus.com is such a terrible resource. There is no `` in the C standard. To say "in C, abs is only declared in " is just wrong. – zmb Jan 27 '14 at 22:07
  • 3
    I will have learned that cplusplus.com is not a precise enough resource, thanks ! – Pascal T. Jan 27 '14 at 22:11
  • 3
    “which deals with `::abs`, not `std::abs`” – uh, how did you figure that out? – Konrad Rudolph Jan 27 '14 at 22:13
  • cplusplus.com may be somewhat imprecise, but I have yet to see a statement that's explicitly wrong on it. It's by no means a terrible resource. – Appleshell Jan 27 '14 at 22:15
  • @KonradRudolph : I looked at http://www.cplusplus.com/reference/complex/abs/ and I (wrongly ?) inferred that there was a "std::" before functions in the std namespace – Pascal T. Jan 27 '14 at 22:17
  • 1
    @AdamS "In C, abs is only declared in ..." That is explicitly wrong, and is linked in the question, and a source of part of the confusion. – juanchopanza Jan 27 '14 at 22:19
  • @tabstop : yes, were are at the bare abs outside of the std namespace – Pascal T. Jan 27 '14 at 22:21
  • @juanchopanza While is a link to a page that mentions stdlib.h. Reading the "C library" section page, it's explicitly stated: _"Each header file has the same name as the C language version but with a "c" prefix and no extension. For example, the C++ equivalent for the C language header file is "_ – Appleshell Jan 27 '14 at 22:24
  • 1
    Here are the correct references : http://en.cppreference.com/w/cpp/numeric/math/abs and http://en.cppreference.com/w/c/numeric/math/abs – Pascal T. Jan 27 '14 at 22:25
  • 1
    @AdamS The statement is still clearly wrong. `cstdlib` is not a C header. It is as simple as that. – juanchopanza Jan 27 '14 at 22:26
  • 1
    @AdamS Note too that `` doesn't have the same contents as `` in C, and that `` in C++ can differ subtly from `` in C++ (and that pre-C++11, almost no compiler was conformant when it came to ``). – James Kanze Jan 27 '14 at 22:40
  • Oh, I just spend an afternoon debugging program because it was using `abs` (`::abs`) instead of `fabs` or `std::abs`. Is there any compiler warning that can catch this? – alfC Sep 07 '14 at 09:21
  • Ansering myself, yes there is `-Wconversion` that will catch exactly this, that is the implicit conversion of `double` to `int` in `abs(d)`. Taken from http://stackoverflow.com/a/9066697/225186 – alfC Sep 07 '14 at 09:35

2 Answers2

47

The official references say... it's a mess. Pre-C++11 and C11:

  • Officially, including <cmath> introduced nothing in ::; all of the functions were in std::. Practically, only export was less respected, and different compilers did very different things. If you included <cmath>, you used std:: everywhere, or what you got varied from compiler to compiler.

  • C didn't provide any overloads: abs took an int, and was declared in <stdlib.h>, fabs took double, and was declared in <math.h>.

  • If you included <math.h> in C++, it's not clear what you got, but since none of the implementers seemed to care about the standard anyway (see the first point above)...

Roughly speaking, either you included <cmath>, and prefixed all of the uses with std::, or you included <math.h>, and used fabs if you wanted support for floating point (and the various suffixes for types other than int or double).

C++11 and C11 added a few new twists:

  • <cmath> is now allowed (but not required) to introduce the symbols in :: as well. One more thing which can vary depending on the implementation. (The goal here was to make existing implementations conformant.)

  • C has a new header, <tgmath.h>, which uses compiler magic to make the functions in <math.h> behave as if they were overloaded as in C++. (So it doesn't apply to abs, but only to fabs.) This header had not been added to C++, for the obvious reason that C++ doesn't need any compiler magic for this.

All in all, the situation has become slightly worse, and my recommendations above still hold. Include either <math.h> and <stdlib.h>, and use abs/fabs and their derivated (e.g. labs, fabsf, etc.) exclusively, or include <cmath>, and use std::abs exclusively. Anything else, and you'll run into portabiity problems.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • You are right, it's a mess :-) But many thanks for your detailed answer and for the account of the C/C++ history – Pascal T. Jan 27 '14 at 22:48
  • Excellent answer. One that confirms my discover after debugging why code that worked perfectly on a Visual Studio compiled application that computed differences between very similar angles was working, but when the code was ported to linux/gcc suddenly the answer changed. From a c++ perspective of class design (and ignoring the backwards compatibility issue), the expectation that you get abs( x) can really bit you, because it all works till suddenly it doesn't. So to be safe you need to explicitly say which one you want: abs() fabs() or std::abs() – Minok Jul 15 '21 at 22:49
0

I was stucked by this bug for half day!!! I use abs(a double value) in my code. but the result wasn't what expected. Until I change abs to std::abs(the double value above). All results match.

Panda Peng
  • 11
  • 2