Math constants

300 views
Skip to first unread message

l...@compuserve.com

unread,
Mar 4, 2017, 1:32:34 PM3/4/17
to ISO C++ Standard - Future Proposals
Hello everyone,

        Many areas of software development where C++ is actively used involve mathematical calculations. The language inherited a sizable math function library from C, and now has its own numerics library that grows with every release. There is however a small but annoying omission: C++ standard headers do not include definitions of mathematical constants. To this math major it appears to be a very strange gap: wherever common mathematical functions are used, the constants such as  π or e appear quite frequently. Yes, it is not terribly difficult to define a constant, but from the C++ user's perspective this is as if they were served a dish at a restaurant but asked to bring their own salt. 

        True, many math.h headers include M_PI and other constant definitions. I see a few problems with this. First,
the predecessor defines are notoriously unsafe, and the C++ community has been trying to move away from them since its very first days. Second, relying on them is not helpful for code's portability, and sometimes requires an additional macro definition such us 
_USE_MATH_DEFINES. Third, these definition are available only by the grace of the specific vendor implementation. For example, the code fragment

#include <cstdlib>
#define _USE_MATH_DEFINES
#include <cmath>
const double PI = M_PI;

compiles in Microsoft Visual C++ 2015 but errors out in Visual C++ 2017 RC because the M_PI is no longer available.

I think a solution would be very uncontroversial and easy to implement.The header <cmath> should include as part of the std namespace the definitions of common math constants specified at long double precision. For example, depending on the length of long double, that portion of the header may look as follows:

namespace std {
const long double PI = 3.14159265358979323846; // pi
const long double E = 2.71828182845904523536; // e
const long double LOG2E = 1.44269504088896340736; // log2(e)
const long double LOG10E = 0.434294481903251827651; // log10(e)
const long double LN2 = 0.693147180559945309417; // ln(2)
const long double LN10 = 2.30258509299404568402; // ln(10)
const long double SQRT2 = 1.41421356237309504880; // sqrt(2)
}

          - Lev

Vishal Oza

unread,
Mar 4, 2017, 2:16:56 PM3/4/17
to ISO C++ Standard - Future Proposals
I am not sure I would like these values written as manual constants but rather as constexpr constants that are evaluated one during ethier compilation or linking. This has the advantage of being as accurate at the hardware allows while have the same runtime as a constant.

l...@compuserve.com

unread,
Mar 4, 2017, 3:30:40 PM3/4/17
to ISO C++ Standard - Future Proposals
My initial line of thought was that const in this context would be equivalent to constexpr. This appears to be a gray area when it comes to implementations, so you are probably correct. Here is the revision:

namespace std {
constexpr long double PI = 3.14159265358979323846;      // pi
constexpr long double E = 2.71828182845904523536;      // e
constexpr long double LOG2E = 1.44269504088896340736;     // log2(e)
constexpr long double LOG10E = 0.434294481903251827651; // log10(e)
constexpr long double LN2 = 0.693147180559945309417;      // ln(2)
constexpr long double LN10 = 2.30258509299404568402;      // ln(10)
constexpr long double SQRT2 = 1.41421356237309504880;     // sqrt(2)

Daniel Krügler

unread,
Mar 4, 2017, 4:18:14 PM3/4/17
to std-pr...@isocpp.org
2017-03-04 21:30 GMT+01:00 <l...@compuserve.com>:
> My initial line of thought was that const in this context would be
> equivalent to constexpr. This appears to be a gray area when it comes to
> implementations, so you are probably correct. Here is the revision:
>
> namespace std {
> constexpr long double PI = 3.14159265358979323846; // pi
> constexpr long double E = 2.71828182845904523536; // e
> constexpr long double LOG2E = 1.44269504088896340736; // log2(e)
> constexpr long double LOG10E = 0.434294481903251827651; // log10(e)
> constexpr long double LN2 = 0.693147180559945309417; // ln(2)
> constexpr long double LN10 = 2.30258509299404568402; // ln(10)
> constexpr long double SQRT2 = 1.41421356237309504880; // sqrt(2)
> }

Given that we have variable templates now, why should the standard
library provide these constants for a specific floating point type?
Furthermore the UPPERCASE names look like macros, I would instead
suggest to switch to a different spelling (I would be ok if only the
first letter would be uppercase).

Depending on the outcome of CWG issue 1729, we could define pi as
follows (in namespace std, not shown below, concrete values are just
indicated):

template<class T>
extern T pi;

template<>
inline constexpr long double pi<long double> = 3.1415926535897932384...L;

template<>
inline constexpr double pi<double> = 3.1415926535...;

template<>
inline constexpr float pi<float> = 3.141592...F;

- Daniel

l...@compuserve.com

unread,
Mar 4, 2017, 4:40:13 PM3/4/17
to ISO C++ Standard - Future Proposals
Yes, the variable templates would be a logical next step. There may be some additional issues with them, and I would very much prefer such issues not to be an impediment for the core idea. As to the spelling, in my view all upper case doesn't necessarily imply a macro but rather a constant expression. I do not have a strong opinion in that regard however.

3dw...@verizon.net

unread,
Mar 9, 2017, 5:44:33 PM3/9/17
to ISO C++ Standard - Future Proposals


I had dabbled with this after variable templates were added.  I took that route.
I think that this could devolve into endless bikeshed discussions and what constants to include.

Design decisions:
1. variable templates.
2. In analogy with ud literals sub-directories place constants in constants sub-directories.
3. I prefixed the math constants with m_ but c_ for constant may be better.

--------------------------------------------------------------------------
/*
  Variable template math constants for the standard library.
  Edward M. Smith-Rowland <esmith-rowland at alionscience dot com>
  2014-03-12

  We have variable templates in C++14.  In fact pi was offered as a use case for that feature.
  We can do math constants right.  The constants in math.h are double precision
  and promote computations unless you cast or erode long double precision.
  These M_* constants are actually POSIX compatibility - std C++ does not mandate them.
  GCC has extension for long double versions suffixed by 'l'
  (but apparently no float versions with 'f' suffix).
  In any case these macros don't help with generic code.
  There are several bespoke versions of generic template math constants around libstdc++
  using all the pre-variable-template techniques and with all the annoyances outlined
  by the variable template proposal.

  'Grammar' + Bikeshedding
  I find 'm_' gives me flexibility in the 'grammar'.  We could do just '_'.  Or 'c_'.
  Dividing numbers: 1_2 is 1/2, pi_2 is pi/2. Alt. _div_ seems wordy.
  Multiples of pi: Npi (2pi, 4pi).   For some reason nobody cares about multiples of e
  but 2e that would be my proposal for 2*e<> if someone were to want.
  Function args preceded by '_' (unlike math.h!) make things easier to read.  Alt. revert that to math.h.
  Lower case because these are not macros and because that's how we roll in the std library.

  What to put in?
  Help people with intensive calculations.
  Division and sqrt are are more expensive than multiplication and integral powers.
  Logs can take a while too.
  On the other hand, logs and square roots of integers appear in series expansions of various
  functions.  Rather than having just a few, perhaps a substantial table would be in order.

  This is a superset of math.h, <ext/cmath> from libstdc++ (with different grammar).

  Prevent collision with an extra constants namespace after std.
  Add namespace versioning.
  Should I insert an experimental namespace between std and constants?

  Should we default to double?  Doing so might play nice with auto.

  Although it spoils the genericity of the template variable constants
  we could offer typedefs to the different precisions.  I'll hold off for now.

  I made the constants and math_constants name spaces inline for the same reason
  library literal operator namespaces are inline: using std to get the math functions
  should give you access to the constants as well.

  This would go in some <cmath> or another.
 */

// Toy implementation:

#if __cplusplus <= 201103L
# include <bits/c++14_warning.h>
#else

namespace std
{
namespace experimental
{
inline namespace maths_v1
{
inline namespace constants
{
inline namespace math_constants
{
 
/// Constant: @f$ \pi @f$ (see POSIX @c M_PI).
 
template<typename _RealType>
   
constexpr _RealType
    m_pi            
= 3.1415'92653'58979'32384'62643'38327'95028'84195e+0L;

  /// Constant: @f$ \pi / 2 @f$ (see POSIX @c M_PI_2).
  template<typename _RealType>
    constexpr _RealType
    m_pi_2          = 1.5707'
96326'79489'66192'31321'69163'97514'42098e+0L;

 
/// Constant: @f$ \pi / 3 @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_pi_3          
= 1.0471'97551'19659'77461'54214'46109'31676'28063e+0L;

  /// Constant: @f$ \pi / 4 @f$ (see POSIX @c M_PI_4).
  template<typename _RealType>
    constexpr _RealType
    m_pi_4          = 7.8539'
81633'97448'30961'56608'45819'87572'10488e-1L;

 
/// Constant: @f$ 4 \pi / 3 @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_4pi_3        
= 4.1887'90204'78639'09846'16857'84437'26705'12253e+0L;

  /// Constant: @f$ 2 \pi @f$.
  template<typename _RealType>
    constexpr _RealType
    m_2pi           = 6.2831'
85307'17958'64769'25286'76655'90057'68391e+0L;

 
/// Constant: @f$ 4 \pi @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_4pi          
= 1.2566'37061'43591'72953'85057'35331'18011'53678e+1L;

  /// Constant: degrees per radian @f$ 180 / \pi @f$.
  template<typename _RealType>
    constexpr _RealType
    m_deg_rad       = 5.7295'
77951'30823'20876'79815'48141'05170'33237e+1L;

 
/// Constant: radians per degree @f$ \pi / 180 @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_rad_deg      
= 1.7453'29251'99432'95769'23690'76848'86127'13443e-2L;

  /// Constant: @f$ \sqrt{\pi / 2} @f$.
  template<typename _RealType>
    constexpr _RealType
    m_sqrt_pi_2     = 1.2533'
14137'31550'02512'07882'64240'55226'26505e+0L;

 
/// Constant: @f$ 1 / \pi @f$ (see POSIX @c M_1_PI).
 
template<typename _RealType>
   
constexpr _RealType
    m_1_pi          
= 3.1830'98861'83790'67153'77675'26745'02872'40691e-1L;

  /// Constant: @f$ 2 / \pi @f$ (see POSIX @c M_2_PI).
  template<typename _RealType>
    constexpr _RealType
    m_2_pi          = 6.3661'
97723'67581'34307'55350'53490'05744'81383e-1L;

 
/// Constant: @f$ 1 / \sqrt{\pi} @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_1_sqrt_pi    
= 5.6418'95835'47756'28694'80794'51560'77258'58438e-1L;

  /// Constant: @f$ \sqrt{\pi} @f$.
  template<typename _RealType>
    constexpr _RealType
    m_sqrt_pi       = 1.7724'
53850'90551'60272'98167'48334'11451'82797e+0L;

 
/// Constant: @f$ 2 / \sqrt{\pi} @f$ (see POSIX @c M_2_SQRTPI).
 
template<typename _RealType>
   
constexpr _RealType
    m_2_sqrt_pi    
= 1.1283'79167'09551'25738'96158'90312'15451'71688e+0L;

  /// Constant: Euler'
s number @f$ e @f$ (see POSIX @c M_E).
 
template<typename _RealType>
   
constexpr _RealType
    m_e            
= 2.7182'81828'45904'52353'60287'47135'26624'97759e+0L;

  /// Constant: @f$ 1 / e @f$.
  template<typename _RealType>
    constexpr _RealType
    m_1_e           = 3.6787'
94411'71442'32159'55237'70161'46086'74462e-1L;

 
/// Constant: @f$ \log_2(e) @f$ (see POSIX @c M_LOG2E).
 
template<typename _RealType>
   
constexpr _RealType
    m_log2_e        
= 1.4426'95040'88896'34073'59924'68100'18921'37427e+0L;

  /// Constant: @f$ \log_2(10) @f$.
  template<typename _RealType>
    constexpr _RealType
    m_log2_10       = 3.3219'
28094'88736'23478'70319'42948'93901'75867e+0L;

 
/// Constant: @f$ \log_10(2) @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_log10_2      
= 3.0102'99956'63981'19521'37388'94724'49302'67680e-1L;

  /// Constant: @f$ \log_10(e) @f$ (see POSIX @c M_LOG10E).
  template<typename _RealType>
    constexpr _RealType
    m_log10_e       = 4.3429'
44819'03251'82765'11289'18916'60508'22940e-1L;

 
/// Constant: @f$ \log_10(pi) @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_log10_pi      
= 4.9714'98726'94133'85435'12682'88290'89887'36507e-1L;

  /// Constant: @f$ \ln(2) @f$ (see POSIX @c M_LN2).
  template<typename _RealType>
    constexpr _RealType
    m_ln_2          = 6.9314'
71805'59945'30941'72321'21458'17656'80748e-1L;

 
/// Constant: @f$ \ln(3) @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_ln_3          
= 1.0986'12288'66810'96913'95245'23692'25257'04648e+0L;

  /// Constant: @f$ \ln(10)@f$ (see POSIX @c M_LN10).
  template<typename _RealType>
    constexpr _RealType
    m_ln_10         = 2.3025'
85092'99404'56840'17991'45468'43642'07602e+0L;

 
/// Constant: Euler-Mascheroni @f$ \gamma_E @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_gamma_e      
= 5.7721'56649'01532'86060'65120'90082'40243'10432e-1L;

  /// Constant: Golden Ratio @f$ \phi = (1 + \sqrt{5})/2 @f$.
  template<typename _RealType>
    constexpr _RealType
    m_phi           = 1.6180'
33988'74989'48482'04586'83436'56381'17720e+0L;

 
/// Constant: @f$ \sqrt{2}@f$ (see POSIX @c M_SQRT2).
 
template<typename _RealType>
   
constexpr _RealType
    m_sqrt_2        
= 1.4142'13562'37309'50488'01688'72420'96980'78569e+0L;

  /// Constant: @f$ \sqrt{3} @f$.
  template<typename _RealType>
    constexpr _RealType
    m_sqrt_3        = 1.7320'
50807'56887'72935'27446'34150'58723'66945e+0L;

 
/// Constant: @f$ \sqrt{5} @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_sqrt_5        
= 2.2360'67977'49978'96964'09173'66873'12762'35440e+0L;

  /// Constant: @f$ \sqrt{7} @f$.
  template<typename _RealType>
    constexpr _RealType
    m_sqrt_7        = 2.6457'
51311'06459'05905'01615'75363'92604'25706e+0L;

 
/// Constant: @f$ 1 / \sqrt{2}@f$ (see POSIX @c M_SQRT1_2).
 
template<typename _RealType>
   
constexpr _RealType
    m_1_sqrt_2      
= 7.0710'67811'86547'52440'08443'62104'84903'92845e-1L;

  /// Constant: Catalan'
s @f$ G = 1 - 1/9 + 1/25 - 1/49 + 1/81 - ... @f$.
 
template<typename _RealType>
   
constexpr _RealType
    m_catalan      
= 9.1596'55941'77219'01505'46035'14932'38411'07741e-1L;

  /// Constant: @f$ \pi^2/6 @f$.
  template<typename _RealType>
    constexpr _RealType
    m_pi2_6         = 1.6449'
34066'84822'64364'72415'16664'60251'89218e+0L;

} // inline namespace math_constants
} // inline namespace constants
} // inline namespace maths_v1
} // namespace experimental
} // namespace std

#endif // C++14



--------------------------------------------------------------------------

Matthew Woehlke

unread,
Mar 10, 2017, 11:19:34 AM3/10/17
to std-pr...@isocpp.org
On 2017-03-09 17:44, 3dw...@verizon.net wrote:
> I think that this could devolve into endless bikeshed discussions and what
> constants to include.

template <typename T>
class pi_helper
{
static constexpr value = 3.14159...;

constexpr operator T() { return value; }

template <int numerator, int denominator>
constexpr T times() { return numerator * value / denominator; }
}

template <typename T>
inline constexpr pi_helper pi<T>;

...and as QOI, implementations can provide explicit specializations of
any of `times<N,D>()` that make sense. This way the namespace isn't
polluted by bunches of `pi_1_3` and so forth, and it's clearer what
value you are asking for.

For roots, I think something like:

template <typename T, unsigned value>
extern T sqrt;

...makes sense. We should specify that at least a particular set of
these must be available, but this would provide a hook where compilers
can provide an intrinsic such that any square root is available as a
constexpr. (This would also permit compilers to choose to provide them
via an intrinsic that can always compute them, but e.g. using a
variable-precision algorithm that ensures the result is always accurate
within the limits of the requested type.) We can do something similar
for logarithm constants.

I don't think we should have constants for 180/pi and pi/180; having
constexpr functions for these operations will I think lead to better code.

Otherwise, we should definitely have Euler's constant, on principle if
nothing else, but I don't think I've ever used the golden ratio or
anything involving square roots and pi. (When are those useful? When is
pi²/6 useful?)

--
Matthew

3dw...@verizon.net

unread,
Mar 10, 2017, 12:08:23 PM3/10/17
to ISO C++ Standard - Future Proposals


On Friday, March 10, 2017 at 11:19:34 AM UTC-5, Matthew Woehlke wrote:
On 2017-03-09 17:44, 3dw...@verizon.net wrote:
> I think that this could devolve into endless bikeshed discussions and what
> constants to include.

  template <typename T>
  class pi_helper
  {
    static constexpr value = 3.14159...;

    constexpr operator T() { return value; }

    template <int numerator, int denominator>
    constexpr T times() { return numerator * value / denominator; }
  }

  template <typename T>
  inline constexpr pi_helper pi<T>;

...and as QOI, implementations can provide explicit specializations of
any of `times<N,D>()` that make sense. This way the namespace isn't
polluted by bunches of `pi_1_3` and so forth, and it's clearer what
value you are asking for.
 
this is a good idea.  i think a small fraction utiity (or a ratio tool would be nice in any case).
I do think that we should have specializations to cover preexisting M_XYZ macros.


For roots, I think something like:

  template <typename T, unsigned value>
  extern T sqrt;

...makes sense. We should specify that at least a particular set of
these must be available, but this would provide a hook where compilers
can provide an intrinsic such that any square root is available as a
constexpr. (This would also permit compilers to choose to provide them
via an intrinsic that can always compute them, but e.g. using a
variable-precision algorithm that ensures the result is always accurate
within the limits of the requested type.) We can do something similar
for logarithm constants.

I think rootls and logs would benefit greatly from a table like this.
Again, we'll specialize to cover old M_ABC macros etc.


I don't think we should have constants for 180/pi and pi/180; having
constexpr functions for these operations will I think lead to better code.

I actually agree.  And this touches on <units> which we really need.  I'd rather have that.
We'll stay away from unit conversions here going forward.
 

Otherwise, we should definitely have Euler's constant, on principle if
nothing else, but I don't think I've ever used the golden ratio or
anything involving square roots and pi. (When are those useful? When is
pi²/6 useful?)
I've used the golden ratio to make pleasantly sized screens.
The others crop up in computing zeta functions, gamma functions, the error function, etc.

--
Matthew

One thing you touched on up there has been bothering me.  I've been working on a batter implementation of math special functions.
I have been trying to leave room for multi-precision reals.  So pi, sqrt3 depend on the chosen precision which is not part of the type in most sane implementations.  mpfr has functions for these.  So anyway, what I've been using in my special function library is functions:
constexpr _Tp __const_pi(Tp _proto = _Tp{})

This will route by ADL (which is nice) to the constant above and be truly constexpr for built-in primitive types.
The mpreal version won't be actually constexpr but the precision is extracted from the prototype (which isn't otherwise used)
and used to call mpfr_pi(__proto.getPrecision()).

Do you have any better Idea?  This is a Fortranesque way of doing things but it works and is type generic.  Fortran (and I) also do this for numeric_limits objects:
constexpr Tp lim_epsilon(const Tp& proto)
routes to numeric_limits<Tp> for float, double, long double, but can call mpreal too.

I should hold off for now on this.

The variable template approach stands on it's own and I don't think it would block what I've described just above.  It hasn't so far.

Ed
 

Beman Dawes

unread,
Mar 29, 2017, 7:04:35 AM3/29/17
to std-proposals
On Sat, Mar 4, 2017 at 1:32 PM, <l...@compuserve.com> wrote:
Hello everyone,

        Many areas of software development where C++ is actively used involve mathematical calculations. The language inherited a sizable math function library from C, and now has its own numerics library that grows with every release. There is however a small but annoying omission: C++ standard headers do not include definitions of mathematical constants. To this math major it appears to be a very strange gap: wherever common mathematical functions are used, the constants such as  π or e appear quite frequently. Yes, it is not terribly difficult to define a constant, but from the C++ user's perspective this is as if they were served a dish at a restaurant but asked to bring their own salt. 


Hi Lev,


IIRC, development of a truly useful library of C++ math functions was far more complex than anyone would have guessed at the onset, and took several years of effort. You might want to talk to John Maddock, Paul Bristow, and Christopher Kormanyos first.

--Beman

Lev Minkovsky

unread,
Mar 29, 2017, 7:33:51 AM3/29/17
to std-pr...@isocpp.org, beman...@gmail.com

Hi Beman,

 

             Thank you for your message. Yes, I am aware of the good work that Boost contributors have done on Boost constants. It doesn’t surprise me that math functions are difficult to implement in a true cross-platform and computationally stable way. I spoke with Paul Bristow via email and got some useful feedback from him.

 

             The goal of my proposal is far more narrow. I focus specifically on constants, and explicitly state that several aspects of their implementation should be left up to the implementer’s discretion. It is not my intent at all to compile a comprehensive set of math constants. I merely want to “push downstream” the set included in several derivative standardization efforts.

 

  • Lev

--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/FbSyFYzsoYI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAAygHNOvBFjUryaTbp7UT-d1mVyt8vXYptCatOpfD1DgGs-m8A%40mail.gmail.com.

l...@compuserve.com

unread,
Mar 29, 2017, 7:34:10 AM3/29/17
to ISO C++ Standard - Future Proposals, bda...@acm.org

Hi Beman,

 

             Thank you for your message. Yes, I am aware of the good work that Boost contributors have done on Boost constants. It doesn’t surprise me that math functions are difficult to implement in a true cross-platform and computationally stable way. I spoke with Paul Bristow via email and got some useful feedback from him.

 

             The goal of my proposal is far more narrow. I focus specifically on constants, and explicitly state that several aspects of their implementation should be left up to the implementer’s discretion. It is not my intent at all to compile a comprehensive set of math constants. I merely want to “push downstream” the set included in several derivative standardization efforts.

 

  • Lev

Reply all
Reply to author
Forward
0 new messages