63

I've had a super frustrating bug in my code for the past few weeks. My code would work exactly as expected on my computer, but as soon as I farmed it out to the HPC server, it would produce weird results.

I've boiled it down to this: on my computer (iMac) the function abs() works with floating point numbers, but on the server abs() truncates it to an integer.

Example:

Server

abs(-1.1341234) = 1

My Mac

abs(-1.1341234) = 1.1341234

Now I know I can fix this by using fabs(), that's not the question. I looked at the versions of gcc on either machine and this is the output:

Server

g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/apps/software/GCCcore/5.4.0/libexec/gcc/x86_64-unknown-linux-gnu/5.4.0/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: ../configure --enable-languages=c,c++,fortran --enable-lto --enable-checking=release --disable-multilib --enable-shared=yes --enable-static=yes --enable-threads=posix --enable-gold=default --enable-plugins --enable-ld --with-plugin-ld=ld.gold --prefix=/apps/software/GCCcore/5.4.0 --with-local-prefix=/apps/software/GCCcore/5.4.0 --enable-bootstrap --with-isl=/dev/shm/GCCcore/5.4.0/dummy-/gcc-5.4.0/stage2_stuff
Thread model: posix
gcc version 5.4.0 (GCC) 

My Mac

g++ -v
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.3)
Target: x86_64-apple-darwin18.5.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

So my question, why does abs() produce different results between gcc and clang? This issue literally cost me 3 weeks of progress, so as you can imagine I'm a little salty just now...

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
Bamboo
  • 973
  • 7
  • 17
  • 14
    _so as you can imagine I'm a little salty just now..._ Annoying but worth the lecture... This will make you very sensitive about these things. ;-) (I don't mean this offensive but rather as a "heads up". Every really annoying bug might at least produce a valuable experience. So, take it positively...) – Scheff's Cat Apr 30 '19 at 08:31
  • 3
    @Scheff Oh man, I certainly won't ever forget this lesson... – Bamboo Apr 30 '19 at 08:46
  • @Phill I have collected some of them in my mind ...and sometimes tell my colleagues my stories of old about this... ;-) – Scheff's Cat Apr 30 '19 at 08:47
  • 5
    @Scheff **No**. A *good programming language* should not have such quirks. (There's *still* a lesson to be learned here, but I won't tell which one ;-) ) – Marco13 Apr 30 '19 at 10:09
  • 1
    related: https://developers.redhat.com/blog/2016/02/29/why-cstdlib-is-more-complicated-than-you-might-think/ – sudo rm -rf slash Apr 30 '19 at 14:37
  • 7
    `-Wall`, fix all warnings. Always. – Ben Apr 30 '19 at 19:01

1 Answers1

91

You must use std::abs, which has overloads for the primitive types.

You are falling through to the C++ version in one case, and the old C version (which converts its parameters to integral types) in the other.

Things to avoid: (1) using namespace std; (here is why) and (2) not having the appropriate #includes to bring in the functionality you need. Don't rely on C++ standard library implementations to include files for you implicitly.

Some compilers will warn you of a "lossy" conversion if you set the warning level appropriately.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • What do you mean with "you _must_ use `std::abs`"? Using `fabs` is also fine, isn't it? – Benjamin Bihler Apr 30 '19 at 09:23
  • 35
    @BenjaminBihler: That kicks the can down the road - fabs breaks for `long double` for example. – Bathsheba Apr 30 '19 at 09:24
  • 7
    @BenjaminBihler if you are using c++ there is normally no good reason not to use the c++ version of the functions. Keep C to C and C++ to C++. – Kami Kaze Apr 30 '19 at 12:54
  • Thank you both. I have been using `fabs` for a long time. The advantage of `std::abs` is indeed something I haven't been aware of. – Benjamin Bihler Apr 30 '19 at 13:07
  • 2
    Correction: Use `fabs` instead of `abs` OR `std::abs` which is a template function. – mid Apr 30 '19 at 17:30
  • 9
    *All* compilers should warn you of a floating-point to integer, or integer to floating-point, conversion. If your compiler doesn't, and you can't adjust its warning levels accordingly, discard it immediately and get yourself a better compiler. – Cody Gray - on strike May 01 '19 at 02:29
  • Also know that unfortunately, this is a long-known issue. See for example https://stackoverflow.com/questions/21392627/abs-vs-stdabs-what-does-the-reference-say from five years ago. That discussion already pointed out the ambiguity of ::abs and ::std::abs, that different compilers behave differently, and that none of them warn you that something fishy is going on. – Nadav Har'El May 01 '19 at 12:23
  • 2
    @Rakete1111: It's lossy in full generality. E.g. 64 bit `int`, and IEEE754 `double`. – Bathsheba May 01 '19 at 12:44