Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use macro definitions (e.g. M_PI) in math.h as float not double

Tags:

c++

gcc

I noticed that all the mathematical constants are declared as double, which leads to conversion problems on some platforms where no double precision units exist. Is there a switch in the standard library of GCC to automatically down-cast or use separate definitions?

like image 617
dgrat Avatar asked Nov 21 '22 15:11

dgrat


1 Answers

C++ Numbers (since C++20)

As mentioned be Bob below, since C++20 we have std::numbers. This would result in:

#include <numbers>

    ...
    double PI = std::number::pi;
    float PIF = std::number::pi_v<float>;
    ...

This will probably be the way to move forward as time passes, although the Boost numbers have many more numbers predefined.


Boost Numbers

Alan Stroke's comment has a link to boost constants which, I think, gives what is the best answer. Use:

#include <boost/math/constants/constants.hpp>

    ...
    boost::math::float_constants::pi
    ...

Similarly, you can make use of double and long double constants:

    ...
    boost::math::double_constants::pi
    ...
    boost::math::long_double_constants::pi
    ...

Internally, Boost uses a macro which does the equivalent of:

M_PI ## F

In other words, it tells the compiler to read that floating point literal as a float instead of a double.


Macro (not recommended)

If you prefer to stick with C-like constants, you could also declare such like so:

#define M_PIF 3.141592653589793238462643383279502884e+00F

Notice the F at the end of the number.

which is pretty much what boost does internally. This may actually be the best solution in your situation since you are saying some of the compilers you use do not support double at all (and thus just using M_PI may already be an issue to start with and the boost library declares all three types: float, double, and long double).

If necessary, the precision of the value can be reduced in case the compiler complains that the literal has too many digits.


Cast

You could also simply cast the default value, however, the compiler still needs to properly support double to some extend:

    ...
    static_cast<float>(M_PI)
    ...

Such a simple cast will prevent the compiler from transforming your numbers to double and then back to float, which in most cases will make things run faster.

Note that for some numbers a cast may not result in the same value as using the F suffix. However, for M_PI, it happens to work just fine:

// create pi.cpp
#include <iostream>
int main(int argc, char * argv[])
{
    float pi1 = 3.14159265358979323846;
    float pi2 = 3.14159265358979323846f;

    std::cout << "pi1: " << pi1 << "\n";
    std::cout << "pi2: " << pi2 << "\n";
    std::cout << "pi1 == pi2? " << std::boolalpha << (pi1 == pi2) << "\n";

    return 0;
}

And the output:

$ g++ -std=c++17 pi.cpp
$ ./a.out
pi1: 3.14159
pi2: 3.14159
pi1 == pi2? true

Well, at least on an Intel processor. The difference should anyway be one bit either way, which is probably not a concern in most situations.

like image 98
Alexis Wilke Avatar answered Dec 21 '22 12:12

Alexis Wilke