Reputation: 6274
I noticed that appleclang++ (v14.0.0) seems to return a single precision float when passing a float input to cmath
's sqrt
. I was surprised to get different results when switching to gcc/clang.
Here's my minimal reproducible example:
#include <cmath>
#include <cstdio>
#include <type_traits>
int main()
{
float f = 1+1e-7;
auto X = sqrt(f);
printf("%16s says X is a %6s with value: %0.17g\n",
#ifdef __clang__
#if defined(__apple_build_version__)
"appleclang++",
#else
"clang++",
#endif
#elif __GNUC__
"g++",
#else
"unknown compiler",
#endif
std::is_same<float, decltype(X)>::value ? "float" : "double",
X);
}
For different compilers I see:
appleclang++ says X is a float with value: 1
clang++ says X is a double with value: 1.000000059604643
g++ says X is a double with value: 1.000000059604643
I'm compiling with -std=c++11
just to be sure.
If I switch to std::sqrt
then everyone agrees it's a float, but I'm still curious. Is this behavior of sqrt
indeed left up to the compiler or is appleclang being non-standard?
Upvotes: 3
Views: 248
Reputation: 76889
Generally, for most math functions, the C++ standard library has one overload for each floating point type that takes that type as parameter and also returns the same type. In addition there are overloads so that an integer argument will behave as if it was converted to double
before being passed.
However, the problem here is that you use sqrt
from the global namespace scope, which is wrong. It should be std::sqrt
.
When including <cmath>
you are only guaranteed that all overloads of std::sqrt
are provided in the std::
namespace scope. You need to include <math.h>
to guarantee that all overloads are available in the global namespace scope and you need to include both to assure that all overloads are available in both the global and std::
namespace scope. This affects all standard library headers derived from C btw.
I would however advice against using (solely) the <math.h>
header as it lacks some other C++-specific math functions and also because it had been deprecated since C++98 and up until including C++20 before being de-deprecated for C++23. Similarly other <_.h>
standard library headers had also been deprecated and lack certain parts that are included in their corresponding <c_>
header. Unfortunately global namespace pollution can however not be avoided even when only including <c_>
headers.
When mismatching the include, it is unspecified which overloads (if any) will be visible and then it is possible that instead of getting the float sqrt(float)
overload you only get the double sqrt(double)
overload, or any other one for that matter, e.g. one that expects an int
and returns a double
, which would cause very weird results.
(Although that last example is a bit far fetched, because there is no good implementation reason that would result in this behavior. The implementation reason for the double
overload being special is that it is supplied by the underlying C standard library implementation, typically part of the system, rather than the C++ standard library implementation which is typically built separately on top of it.)
It is also possible that no overload will be visible and the program won't compile.
Upvotes: 4