By accident I was calling round()
and fabs()
instead of std::round()
and std::fabs()
and for the largest integer a long double
can hold without loosing precision there was a difference.
Consider this test program round.cpp
:
#include <iostream>
#include <iomanip>
#include <cstdint>
#include <limits>
#include <cmath>
using std::cout;
using std::endl;
using std::setw;
using std::setprecision;
void print(const char* msg, const long double ld)
{
cout << msg << setprecision(20) << ld << endl;
}
void test(const long double ld)
{
const long double ldRound = round(ld);
const long double ldStdRound = std::round(ld);
const long double ldFabs = fabs(ld);
const long double ldStdFabs = std::fabs(ld);
print("Rounding using 'round()': ", ldRound);
print("Rounding using 'std::round()': ", ldStdRound);
print("Absolute value using 'fabs()': ", ldFabs);
print("Absolute value using 'std::fabs()': ", ldStdFabs);
}
int main()
{
const int maxDigits = std::numeric_limits<long double>::digits;
const int64_t maxPosInt = 0xffffffffffffffff >> (64 - maxDigits + 1);
const long double maxPosLongDouble = (long double) maxPosInt;
cout << setw(20);
cout << "Max decimal digits in long double: " << maxDigits << endl;
cout << "Max positive integer to store in long double: " << maxPosInt << endl;
print("Corresponding long double: ", maxPosLongDouble);
test(maxPosLongDouble);
return 0;
}
When compiling with g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36.0.1)
/usr/bin/g++ -std=c++11 round.cpp -o round
and then running it, the results are one larger for the non-std
function compared to the std
functions:
Max decimal digits in long double: 64
Max positive integer to store in long double: 9223372036854775807
Corresponding long double: 9223372036854775807
Rounding using 'round()': 9223372036854775808 <== one larger
Rounding using 'std::round()': 9223372036854775807
Absolute value using 'fabs()': 9223372036854775808 <== one larger
Absolute value using 'std::fabs()': 9223372036854775807
I get the exact same output (including 64 bits for long double) when I compile for 32 bits using option -m32
. Looking at the disassembly (using gbd
on the 32 bit executable) for function test()
I get:
(gdb) disassemble test(long double)
Dump of assembler code for function _Z4teste:
0x080488c0 <+0>: push %ebp
...
0x080488d2 <+18>: call 0x8048690 <round@plt>
...
0x080488ee <+46>: call 0x8048b59 <_ZSt5rounde> (demangled: std::round(long double))
...
0x080488ff <+63>: fabs
...
0x08048918 <+88>: call 0x8048b4f <_ZSt4fabse> (demangled: std::fabs(long double))
...
0x080489a4 <+228>: leave
0x080489a5 <+229>: ret
End of assembler dump.
So it seems different function are called for round()
and std::round()
. For fabs()
a floating point instruction is emitted whereas for std::fabs()
a function call is emitted.
Can someone explain what is causing this difference and please tell me whether using std::round()
and std::fabs()
is the preferred portable choice?