Most of C++26 is based on C23, but the header <cmath>
is still based on C17.
There are many useful C23 <math.h> features that should be provided in C++.
Contents
1. Introduction
[P3348R4] rebased the C++26 standard on C23;
it previously referred to C17.
However, this process was deliberately left incomplete:
some of the new C23 <math.h> features are only used by decimal floating-point types,
or they require core language changes, etc.
The goal of this proposal is to pull in all the new C23 <math.h> features
which are useful not only to decimal floating-point numbers.
Introducing decimal floating-point numbers into C++ is out-of scope.
That is, adding C's _Decimal128 type as a std::decimal128_t alias
is not proposed.
However, this proposal lays a bit of groundwork to make that happen in the future,
such as introducing the concept of canonical representations into the core language.
2. Design
2.1. New functions
The functions in the table below are proposed for inclusion in C++29.
The placeholder F is a shorthand for the
floating-point-type placeholder in [cmath.syn].
In addition to the functions using an F placeholder,
single-type functions such as float acospif(float) are proposed,
but are not listed in the table for the sake of brevity;
refer to §4. Wording for the complete list.
| Function |
Returns |
bool iscanonical(F x); |
true if and only if x is canonical
|
bool issignaling(F x); |
true if and only if x is a signaling NaN
|
bool issubnormal(F x); |
true if and only if x is subnormal
|
bool iszero(F x); |
true if and only if x is zero
|
F acospi(F x); |
in the interval
|
F asinpi(F x); |
in the interval
|
F atanpi(F x); |
in the interval
|
F atan2pi(F y, F x); |
in the interval
|
F cospi(F x); |
|
F sinpi(F x); |
|
F tanpi(F x); |
|
F exp10(F x); |
|
F exp10m1(F x); |
|
F exp2m1(F x); |
|
F log10p1(F x); |
|
F logp1(F x); |
|
F log2p1(F x); |
|
F compoundn(F x, long long n); |
|
F pown(F x, long long n); |
|
F powr(F y, F x); |
|
F rootn(F x, long long n); |
|
F rsqrt(F x); |
|
F roundeven(F x); |
x rounded to the nearest integer,
rounding halfway cases to even
|
F fromfp(F x, int rnd, unsigned width); |
x rounded to a signed width-bit integer,
or NaN if x is not in range;
rnd is the rounding mode
|
F ufromfp(F x, int rnd, unsigned width); |
x rounded to an unsigned width-bit integer,
or NaN if x is not in range;
rnd is the rounding mode
|
F fromfpx(F x, int rnd, unsigned width); |
fromfp(x, rnd, width);
may also raise FE_INEXACT
|
F ufromfpx(F x, int rnd, unsigned width); |
ufromfp(x, rnd, width);
may also raise FE_INEXACT
|
bool canonicalize(F* cx, const F* x); |
true if and only if canonicalization failed
|
F fmaximum_mag(F x, F y); |
the value with greater magnitude,
or fmaximum(x, y)
|
F fminimum_mag(F x, F y); |
the value with greater magnitude,
or fminimum(x, y)
|
F fmaximum_mag_num(F x, F y); |
the value with greater magnitude,
or fmaximum_num(x, y)
|
F fminimum_mag_num(F x, F y); |
the value with greater magnitude,
or fminimum_num(x, y)
|
float fadd(F x, F y);
double dadd(F x, F y);
|
|
float fsub(F x, F y);
double dsub(F x, F y);
|
|
float fmul(F x, F y);
double dmul(F x, F y);
|
|
float fdiv(F x, F y);
double ddiv(F x, F y);
|
|
float ffma(F x, F y, F z);
double dfma(F x, F y, F z);
|
|
float fsqrt(F x);
double dsqrt(F x);
|
|
bool iseqsig(F x, F y) |
true if x and y are equal and
false otherwise
|
2.1.1. Narrow rounding functions
The narrow rounding functions (such as fadd)
are unusually difficult to expose in C++ in a generic way.
Even the <tgmath.h> versions require the user to specify the return type
as a prefix (f, d, f32, …) as part of the function name.
This is extremely hostile to generic code,
and also means there is no support for extended floating-types in C++
because we don't consider std::float32_t to be C23's _Float32
and to have sqrtf32 and other functions that accept it.
To solve this, we can do something novel:
add function templates for these rounding functions:
template<class T, class F1, class F2>
constexpr T fadd(F1 x, F2 y) noexcept;
template<class T, class F1, class F2>
constexpr T fsub(F1 x, F2 y) noexcept;
template<class T, class F1, class F2>
constexpr T fmul(F1 x, F2 y) noexcept;
template<class T, class F1, class F2>
constexpr T fdiv(F1 x, F2 y) noexcept;
template<class T, class F1, class F2, class F3>
constexpr T ffma(F1 x, F2 y, F3 z) noexcept;
template<class T, class F1>
constexpr T fsqrt(F1 x) noexcept;
While the prefix f
is a bit inappropriate
(it it supposed to indicate a float return type),
there is no obvious alternative prefix,
f
definitely won't clash with future <math.h> functions,
and having no prefix at all would create undesirable overload sets with other functions.
The design goal for these functions is for the user to be able to write:
#ifdef __cplusplus
#define f32add(...) ::std::fadd<std::float32_t>(__VA_ARGS__)
// ...
#else
#include <tgmath.h>
#endif
There is also a non-template std::fadd function
which is meant to emulate the behavior of the <tgmath.h> macro
by selecting between the long double→float and double→float
overloads.
2.1.2. Canonical floating-point representations and canonicalization
C23 introduced the concept of "canonical representations"
identified by iscanonical,
and a function canonicalize
to convert to canonical representations of the same value.
Examples of canonical/non-canonical representations include:
-
Representations for decimal floating-point types are specified
to be canonical or non-canonical by ISO/IEC 60559.
- Signaling NaN may be non-canonical, whereas quiet NaN may be canonical.
-
An implementation that flushes subnormals to zero
may consider all subnormal normals to be non-canonical representations of zero.
-
Negative zero may be considered a non-canonical representation
of positive zero, which is canonical.
Unless working with decimal floating-point types,
the classification of representations as "canonical"
is largely based on the subjective preferences of the implementation;
there are no hard rules other than a canonical representation being unique.
2.2. New macros
Besides the new functions,
there are also macros which are pulled in from C23.
The int rnd parameter in functions such as fromfp
corresponds to one of FP_INT_* rounding direction macros.
The block below lists all new macros:
// rounding direction macros
FP_INT_UPWARD
FP_INT_DOWNWARD
FP_INT_TOWARDZERO
FP_INT_TONEARESTFROMZERO
FP_INT_TONEAREST
// indicates that the respective function is "fast"
FP_FAST_FADD
FP_FAST_FADDL
FP_FAST_DADDL
FP_FAST_FSUB
FP_FAST_FSUBL
FP_FAST_DSUBL
FP_FAST_FMUL
FP_FAST_FMULL
FP_FAST_DMULL
FP_FAST_FDIV
FP_FAST_FDIVL
FP_FAST_DDIVL
FP_FAST_FSQRT
FP_FAST_FSQRTL
FP_FAST_DSQRTL
FP_FAST_FFMA
FP_FAST_FFMAL
FP_FAST_DFMAL
3. Implementation experience
All non-template additions are taken from C23,
and most have been implemented in gnulibc.
The new narrow rounding function templates simply dispatch to some
non-template overload based on the template argument T.
4. Wording
The changes are relative to [N5014].
[basic.fundamental]
Immediately prior to [basic.fundamental] paragraph 13,
insert a new paragraph:
An implementation may prefer particular representations of values
that have multiple representations in a floating-point type.
The preferred representations of a floating-point type,
including unique representations of values in the type, are called canonical.
A floating-point type may also contain non-canonical representations,
for example, redundant representations of some or all its
values, or representations that are extraneous to the floating-point model.
Typically, floating-point operations deliver results with canonical representations.
[Note:
The functions std::iscanonical and std::canonicalize distinguish
canonical (preferred) representations,
but this distinction alone does not imply that canonical and non-canonical representations
are of different values.
— end note]
The C23 wording is extremely vague,
and we can't be precise either if we don't want to risk having diverging
behavior for iscanonical.
iscanonical and canonicalize are primarily motivated by
ISO/IEC 60559 decimal floating-point representations anyway,
where it is precisely specified which representations are canonical.
[version.syn]
Add a feature-test macro to [version.syn] as follows:
#define __cpp_lib_cmath 20XXXXL // also in <cmath>, <cstdlib>
Although the proposal also adds a __STDC_VERSION_MATH_H__ macro from C23,
this cannot be reliably used to detect the new functions;
it could have also been leaked through a <math.h> header from somewhere,
without e.g. std::roundeven actually existing.
[cmath.syn]
#define __STDC_VERSION_MATH_H__ 202311L
#define HUGE_VAL see below
#define HUGE_VALF see below
#define HUGE_VALL see below
#define INFINITY see below
#define NAN see below
#define FP_INFINITE see below
#define FP_NAN see below
#define FP_NORMAL see below
#define FP_SUBNORMAL see below
#define FP_ZERO see below
#define FP_INT_UPWARD see below
#define FP_INT_DOWNWARD see below
#define FP_INT_TOWARDZERO see below
#define FP_INT_TONEARESTFROMZERO see below
#define FP_INT_TONEAREST see below
#define FP_FAST_FMA see below
#define FP_FAST_FMAF see below
#define FP_FAST_FMAL see below
#define FP_FAST_FADD see below
#define FP_FAST_FADDL see below
#define FP_FAST_DADDL see below
#define FP_FAST_FSUB see below
#define FP_FAST_FSUBL see below
#define FP_FAST_DSUBL see below
#define FP_FAST_FMUL see below
#define FP_FAST_FMULL see below
#define FP_FAST_DMULL see below
#define FP_FAST_FDIV see below
#define FP_FAST_FDIVL see below
#define FP_FAST_DDIVL see below
#define FP_FAST_FSQRT see below
#define FP_FAST_FSQRTL see below
#define FP_FAST_DSQRTL see below
#define FP_FAST_FFMA see below
#define FP_FAST_FFMAL see below
#define FP_FAST_DFMAL see below
#define FP_ILOGB0 see below
#define FP_ILOGBNAN see below
#define MATH_ERRNO see below
#define MATH_ERREXCEPT see below
#define math_errhandling see below
namespace std {
using float_t = see below;
using double_t = see below;
constexpr floating-point-type acos(floating-point-type x);
constexpr float acosf(float x);
constexpr long double acosl(long double x);
constexpr floating-point-type asin(floating-point-type x);
constexpr float asinf(float x);
constexpr long double asinl(long double x);
constexpr floating-point-type atan(floating-point-type x);
constexpr float atanf(float x);
constexpr long double atanl(long double x);
constexpr floating-point-type atan2(floating-point-type y, floating-point-type x);
constexpr float atan2f(float y, float x);
constexpr long double atan2l(long double y, long double x);
constexpr floating-point-type cos(floating-point-type x);
constexpr float cosf(float x);
constexpr long double cosl(long double x);
constexpr floating-point-type sin(floating-point-type x);
constexpr float sinf(float x);
constexpr long double sinl(long double x);
constexpr floating-point-type tan(floating-point-type x);
constexpr float tanf(float x);
constexpr long double tanl(long double x);
constexpr floating-point-type acospi(floating-point-type x);
constexpr float acospif(float x);
constexpr long double acospil(long double x);
constexpr floating-point-type asinpi(floating-point-type x);
constexpr float asinpif(float x);
constexpr long double asinpil(long double x);
constexpr floating-point-type atanpi(floating-point-type x);
constexpr float atanpif(float x);
constexpr long double atanpil(long double x);
constexpr floating-point-type atan2(floating-point-type y, floating-point-type x);
constexpr float atan2f(float y, float x);
constexpr long double atan2l(long double y, long double x);
constexpr floating-point-type cospi(floating-point-type x);
constexpr float cospif(float x);
constexpr long double cospil(long double x);
constexpr floating-point-type sinpi(floating-point-type x);
constexpr float sinpif(float x);
constexpr long double sinpil(long double x);
constexpr floating-point-type tanpi(floating-point-type x);
constexpr float tanpif(float x);
constexpr long double tanpil(long double x);
constexpr floating-point-type acosh(floating-point-type x);
constexpr float acoshf(float x);
constexpr long double acoshl(long double x);
constexpr floating-point-type asinh(floating-point-type x);
constexpr float asinhf(float x);
constexpr long double asinhl(long double x);
constexpr floating-point-type atanh(floating-point-type x);
constexpr float atanhf(float x);
constexpr long double atanhl(long double x);
constexpr floating-point-type cosh(floating-point-type x);
constexpr float coshf(float x);
constexpr long double coshl(long double x);
constexpr floating-point-type sinh(floating-point-type x);
constexpr float sinhf(float x);
constexpr long double sinhl(long double x);
constexpr floating-point-type tanh(floating-point-type x);
constexpr float tanhf(float x);
constexpr long double tanhl(long double x);
constexpr floating-point-type exp(floating-point-type x);
constexpr float expf(float x);
constexpr long double expl(long double x);
constexpr floating-point-type exp10(floating-point-type x);
constexpr float exp10f(float x);
constexpr long double exp10l(long double x);
constexpr floating-point-type exp10m1(floating-point-type x);
constexpr float exp10m1f(float x);
constexpr long double exp10m1l(long double x);
constexpr floating-point-type exp2(floating-point-type x);
constexpr float exp2f(float x);
constexpr long double exp2l(long double x);
constexpr floating-point-type exp2m1(floating-point-type x);
constexpr float exp2m1f(float x);
constexpr long double exp2m1l(long double x);
constexpr floating-point-type expm1(floating-point-type x);
constexpr float expm1f(float x);
constexpr long double expm1l(long double x);
constexpr floating-point-type frexp(floating-point-type value, int* exp);
constexpr float frexpf(float value, int* exp);
constexpr long double frexpl(long double value, int* exp);
constexpr int ilogb(floating-point-type x);
constexpr int ilogbf(float x);
constexpr int ilogbl(long double x);
constexpr floating-point-type ldexp(floating-point-type x, int exp);
constexpr float ldexpf(float x, int exp);
constexpr long double ldexpl(long double x, int exp);
constexpr floating-point-type log(floating-point-type x);
constexpr float logf(float x);
constexpr long double logl(long double x);
constexpr floating-point-type log10(floating-point-type x);
constexpr float log10f(float x);
constexpr long double log10l(long double x);
constexpr floating-point-type log10p1(floating-point-type x);
constexpr float log10p1f(float x);
constexpr long double log10p1l(long double x);
constexpr floating-point-type log1p(floating-point-type x);
constexpr float log1pf(float x);
constexpr long double log1pl(long double x);
constexpr floating-point-type logp1(floating-point-type x);
constexpr float logp1f(float x);
constexpr long double logp1l(long double x);
constexpr floating-point-type log2(floating-point-type x);
constexpr float log2f(float x);
constexpr long double log2l(long double x);
constexpr floating-point-type log2p1(floating-point-type x);
constexpr float log2p1f(float x);
constexpr long double log2p1l(long double x);
constexpr floating-point-type logb(floating-point-type x);
constexpr float logbf(float x);
constexpr long double logbl(long double x);
constexpr floating-point-type modf(floating-point-type value, floating-point-type* iptr);
constexpr float modff(float value, float* iptr);
constexpr long double modfl(long double value, long double* iptr);
constexpr floating-point-type scalbn(floating-point-type x, int n);
constexpr float scalbnf(float x, int n);
constexpr long double scalbnl(long double x, int n);
constexpr floating-point-type scalbln(floating-point-type x, long int n);
constexpr float scalblnf(float x, long int n);
constexpr long double scalblnl(long double x, long int n);
constexpr floating-point-type cbrt(floating-point-type x);
constexpr float cbrtf(float x);
constexpr long double cbrtl(long double x);
constexpr floating-point-type compoundn(floating-point-type x, long long int n);
constexpr float compoundn(float x, long long int n);
constexpr long double compoundn(long double x, long long int n);
// [c.math.abs], absolute values
constexpr int abs(int j); // freestanding
constexpr long int abs(long int j); // freestanding
constexpr long long int abs(long long int j); // freestanding
constexpr floating-point-type abs(floating-point-type j); // freestanding-deleted
constexpr floating-point-type fabs(floating-point-type x);
constexpr float fabsf(float x);
constexpr long double fabsl(long double x);
constexpr floating-point-type hypot(floating-point-type x, floating-point-type y);
constexpr float hypotf(float x, float y);
constexpr long double hypotl(long double x, long double y);
// [c.math.hypot3], three-dimensional hypotenuse
constexpr floating-point-type hypot(floating-point-type x, floating-point-type y,
floating-point-type z);
constexpr floating-point-type pow(floating-point-type x, floating-point-type y);
constexpr float powf(float x, float y);
constexpr long double powl(long double x, long double y);
constexpr floating-point-type pown(floating-point-type x, long long int n);
constexpr float pownf(float x, long long int n);
constexpr long double pownl(long double x, long long long int n);
constexpr floating-point-type powr(floating-point-type x, floating-point-type y);
constexpr float powrf(float x, float y);
constexpr long double powrl(long double x, long double y);
constexpr floating-point-type rootn(floating-point-type x);
constexpr float rootnf(float x);
constexpr long double rootnl(long double x);
constexpr floating-point-type rsqrt(floating-point-type x);
constexpr float rsqrtf(float x);
constexpr long double rsqrtl(long double x);
constexpr floating-point-type sqrt(floating-point-type x);
constexpr float sqrtf(float x);
constexpr long double sqrtl(long double x);
constexpr floating-point-type erf(floating-point-type x);
constexpr float erff(float x);
constexpr long double erfl(long double x);
constexpr floating-point-type erfc(floating-point-type x);
constexpr float erfcf(float x);
constexpr long double erfcl(long double x);
constexpr floating-point-type lgamma(floating-point-type x);
constexpr float lgammaf(float x);
constexpr long double lgammal(long double x);
constexpr floating-point-type tgamma(floating-point-type x);
constexpr float tgammaf(float x);
constexpr long double tgammal(long double x);
constexpr floating-point-type ceil(floating-point-type x);
constexpr float ceilf(float x);
constexpr long double ceill(long double x);
constexpr floating-point-type floor(floating-point-type x);
constexpr float floorf(float x);
constexpr long double floorl(long double x);
floating-point-type nearbyint(floating-point-type x);
float nearbyintf(float x);
long double nearbyintl(long double x);
floating-point-type rint(floating-point-type x);
float rintf(float x);
long double rintl(long double x);
long int lrint(floating-point-type x);
long int lrintf(float x);
long int lrintl(long double x);
long long int llrint(floating-point-type x);
long long int llrintf(float x);
long long int llrintl(long double x);
constexpr floating-point-type round(floating-point-type x);
constexpr float roundf(float x);
constexpr long double roundl(long double x);
constexpr long int lround(floating-point-type x);
constexpr long int lroundf(float x);
constexpr long int lroundl(long double x);
constexpr long long int llround(floating-point-type x);
constexpr long long int llroundf(float x);
constexpr long long int llroundl(long double x);
constexpr floating-point-type roundeven(floating-point-type x);
constexpr float roundevenf(float x);
constexpr long double roundevenl(long double x);
constexpr floating-point-type trunc(floating-point-type x);
constexpr float truncf(float x);
constexpr long double truncl(long double x);
constexpr floating-point-type fromfp(floating-point-type x, int rnd, unsigned int width);
constexpr float fromfpf(float x, int rnd, unsigned int width);
constexpr long double fromfpl(long double x, int rnd, unsigned int width);
constexpr floating-point-type ufromfp(floating-point-type x, int rnd, unsigned int width);
constexpr float ufromfpf(float x, int rnd, unsigned int width);
constexpr long double ufromfpl(long double x, int rnd, unsigned int width);
constexpr floating-point-type fromfpx(floating-point-type x, int rnd, unsigned int width);
constexpr float fromfpxf(float x, int rnd, unsigned int width);
constexpr long double fromfpxl(long double x, int rnd, unsigned int width);
constexpr floating-point-type ufromfpx(floating-point-type x, int rnd, unsigned int width);
constexpr float ufromfpxf(float x, int rnd, unsigned int width);
constexpr long double ufromfpxl(long double x, int rnd, unsigned int width);
constexpr floating-point-type fmod(floating-point-type x, floating-point-type y);
constexpr float fmodf(float x, float y);
constexpr long double fmodl(long double x, long double y);
constexpr floating-point-type remainder(floating-point-type x, floating-point-type y);
constexpr float remainderf(float x, float y);
constexpr long double remainderl(long double x, long double y);
constexpr floating-point-type remquo(floating-point-type x, floating-point-type y, int* quo);
constexpr float remquof(float x, float y, int* quo);
constexpr long double remquol(long double x, long double y, int* quo);
constexpr floating-point-type copysign(floating-point-type x, floating-point-type y);
constexpr float copysignf(float x, float y);
constexpr long double copysignl(long double x, long double y);
double nan(const char* tagp);
float nanf(const char* tagp);
long double nanl(const char* tagp);
constexpr floating-point-type nextafter(floating-point-type x, floating-point-type y);
constexpr float nextafterf(float x, float y);
constexpr long double nextafterl(long double x, long double y);
constexpr floating-point-type nexttoward(floating-point-type x, long double y);
constexpr float nexttowardf(float x, long double y);
constexpr long double nexttowardl(long double x, long double y);
constexpr floating-point-type nextup(floating-point-type x);
constexpr float nextupf(float x);
constexpr long double nextupl(long double x);
constexpr floating-point-type nextdown(floating-point-type x);
constexpr float nextdownf(float x);
constexpr long double nextdownl(long double x);
constexpr bool canonicalize(floating-point-type* cx, const floating-point-type* x);
constexpr bool canonicalizef(float cx, const float* x);
constexpr bool canonicalizel(long double cx, const long double* x);
constexpr floating-point-type fdim(floating-point-type x, floating-point-type y);
constexpr float fdimf(float x, float y);
constexpr long double fdiml(long double x, long double y);
constexpr floating-point-type fmax(floating-point-type x, floating-point-type y);
constexpr float fmaxf(float x, float y);
constexpr long double fmaxl(long double x, long double y);
constexpr floating-point-type fmin(floating-point-type x, floating-point-type y);
constexpr float fminf(float x, float y);
constexpr long double fminl(long double x, long double y);
constexpr floating-point-type fmaximum(floating-point-type x, floating-point-type y);
constexpr floating-point-type fmaximum_num(floating-point-type x, floating-point-type y);
constexpr floating-point-type fmaximum_mag(floating-point-type x, floating-point-type y);
constexpr floating-point-type fmaximum_mag_num(floating-point-type x, floating-point-type y);
constexpr floating-point-type fminimum(floating-point-type x, floating-point-type y);
constexpr floating-point-type fminimum_num(floating-point-type x, floating-point-type y);
constexpr floating-point-type fminimum_mag(floating-point-type x, floating-point-type y);
constexpr floating-point-type fminimum_mag_num(floating-point-type x, floating-point-type y);
constexpr floating-point-type fma(floating-point-type x, floating-point-type y,
floating-point-type z);
constexpr float fmaf(float x, float y, float z);
constexpr long double fmal(long double x, long double y, long double z);
// [c.math.narrow], narrowing operations
template<class T, class F> constexpr T fadd(F x, F y) noexcept;
constexpr float fadd(double-type x, double-type y);
constexpr float faddl(long double x, long double y);
constexpr double dadd(long double x, long double y);
constexpr double daddl(long double x, long double y);
template<class T, class F> constexpr T fsub(F x, F y) noexcept;
constexpr float fsub(double-type x, double-type y);
constexpr float fsubl(long double x, long double y);
constexpr double dsub(long double x, long double y);
constexpr double dsubl(long double x, long double y);
template<class T, class F> constexpr T fmul(F x, F y) noexcept;
constexpr float fmul(double-type x, double-type y);
constexpr float fmull(long double x, long double y);
constexpr double dmul(long double x, long double y);
constexpr double dmull(long double x, long double y);
template<class T, class F> constexpr T fdiv(F x, F y) noexcept;
constexpr float fdiv(double-type x, double-type y);
constexpr float fdivl(long double x, long double y);
constexpr double ddiv(long double x, long double y);
constexpr double ddivl(long double x, long double y);
template<class T, class F> constexpr T ffma(F x, F y, F z) noexcept;
constexpr float ffma(double-type x, double-type y, double-type z);
constexpr float ffmal(long double x, long double y, long double z);
constexpr double dfma(long double x, long double y, long double z);
constexpr double dfmal(long double x, long double y, long double z);
template<class T, class F> constexpr T fsqrt(F x) noexcept;
constexpr float fsqrt(double-type x);
constexpr float fsqrtl(long double x);
constexpr double dsqrt(long double x);
constexpr double dsqrtl(long double x);
// [c.math.lerp], linear interpolation
constexpr floating-point-type lerp(floating-point-type a, floating-point-type b,
floating-point-type t) noexcept;
// [c.math.fpclass], classification / comparison functions
constexpr int fpclassify(floating-point-type x);
constexpr bool iscanonical(floating-point-type x);
constexpr bool isfinite(floating-point-type x);
constexpr bool isinf(floating-point-type x);
constexpr bool isnan(floating-point-type x);
constexpr bool isnormal(floating-point-type x);
constexpr bool signbit(floating-point-type x);
constexpr bool issignaling(floating-point-type x);
constexpr bool issubnormal(floating-point-type x);
constexpr bool iszero(floating-point-type x);
constexpr bool isgreater(floating-point-type x, floating-point-type y);
constexpr bool isgreaterequal(floating-point-type x, floating-point-type y);
constexpr bool isless(floating-point-type x, floating-point-type y);
constexpr bool islessequal(floating-point-type x, floating-point-type y);
constexpr bool islessgreater(floating-point-type x, floating-point-type y);
constexpr bool isunordered(floating-point-type x, floating-point-type y);
constexpr bool iseqsig(floating-point-type x, floating-point-type y);
// [sf.cmath], mathematical special functions
[…]
}
Change [cmath.syn] paragraph 1 as follows:
The contents and meaning of the header <cmath> are a subset of
the C standard library header <math.h>
and only the declarations shown in the synopsis above are present,
with the addition of
a three-dimensional hypotenuse function,
a linear interpolation function, and
the mathematical special functions described in [sf.cmath].
Do not change [cmath.syn] paragraph 2:
For each function with at least one parameter of type floating-point-type,
the implementation provides an overload
for each cv-unqualified floating-point type ([basic.fundamental])
where all uses of floating-point-type in the function signature are replaced
with that floating-point type.
Insert a new paragraph immediately following [cmath.syn] paragraph 2:
For each function with at least one parameter of type double-type,
the implementation provides an overload
for double and long double
where all uses of double-type in the function signature are replaced
with that floating-point type.
Insert a new items following that:
¶
Let common-floating-point-type and
common-double-type
be the following exposition-only alias templates:
template<class... Ts>
using common-floating-point-type = see below;
template<class... Ts>
using common-double-type = see below;
¶
Constraints:
Each type in the pack Ts is a
cv-unqualified arithmetic type ([basic.fundamental]).
¶
Let R be defined as follows:
-
For
common-floating-point-type,
let R be the floating-point type with the
greatest floating-point conversion rank and
greatest floating-point conversion subrank among the types in Ts,
where integer types are considered to have
the same floating-point conversion rank as double.
-
For
common-double-type,
let R be long double if any of the types in Ts
is long double, and
double otherwise.
¶
Mandates:
R is defined.
¶
Result:
R.
common-floating-point-type is just following the existing
semantics of [cmath.syn] paragraph 3 in the form of an exposition-only alias template.
common-double-type is meant to emulate
the behavior of the <tgmath.h> macros fadd, fsub, etc.
For dadd, dsub, etc. no such thing is necessary because without
decimal floating-point types,
the behavior is always to convert all arguments to long double.
Change [cmath.syn] paragraph 3 as follows:
For each function with at least one parameter of type
floating-point-type or double-type
other than abs,
the implementation also provides additional overloads sufficient to ensure that,
if every argument corresponding to a
floating-point-type or double-type
parameter has arithmetic type,
then every such argument is effectively cast to
the floating-point type
with the greatest floating-point conversion rank and
greatest floating-point conversion subrank among the types of all such arguments,
where arguments of integer type are considered to have
the same floating-point conversion rank as double
common-floating-point-type<Args...>
or common-double-type<Args...>, respectively.
If no such floating-point type with the greatest rank and subrank exists
that specialization of the alias template is ill-formed,
then overload resolution does not result in a usable candidate ([over.match.general])
from the overloads provided by the implementation.
Add a new paragraph immediately following [cmath.syn] paragraph 3:
The implementation may provide additional overloads of
padd,
psub,
pmul,
pdiv,
pfma, and
psqrt,
for any prefix p,
with parameters of cv-unqualified arithmetic types
to ensure consistent behavior with the corresponding macro
in C header <tgmath.h>.
While the implementation is generally free to provide additional overloads,
this is not allowed to alter the behavior of a conforming program.
If an implementation provided an extended floating-point type _Decimal128,
dadd would need to convert arguments of type _Decimal128
to long double,
but the C23 behavior is to call daddd128.
This would not be the case if _Decimal128 was a compiler extension,
in which case the program isn't standard C++ anyway,
and the implementation can do whatever it wants.
[c.math.narrow]
Add a new subclause as follows:
Narrowing operations [c.math.narrow]
constexpr float fop(long double x, long double y);
constexpr double dop(long double x, long double y);
¶
Let op be a placeholder for
add, sub, mul, div, fma, or sqrt.
¶
Effects:
Each of the functions
fop and dop
has the same behavior as the functions
fopl and dopl,
respectively.
template<class T, class F1, class F2>
constexpr T fadd(F1 x, F2 y) noexcept;
template<class T, class F1, class F2>
constexpr T fsub(F1 x, F2 y) noexcept;
template<class T, class F1, class F2>
constexpr T fmul(F1 x, F2 y) noexcept;
template<class T, class F1>
constexpr T fdiv(F1 x, F2 y) noexcept;
template<class T, class F1, class F2, class F3>
constexpr T ffma(F1 x, F2 y, F3 z) noexcept;
template<class T, class F1>
constexpr T fsqrt(F1 x) noexcept;
¶
Let Fs be a pack of template type parameters following T.
Let F be the type determined as follows:
-
If
T is double or
if any type in Fs is long double,
F is long double.
-
Otherwise, if
T is float,
F is double.
-
Otherwise, if
T is an extended floating-point type
named by an alias std::floatN_t ([stdfloat.syn]),
F is the type named by std::floatM_t,
where ,
if such an alias is defined.
-
Otherwise,
F is implementation-defined
and may not exist.
¶
Constraints:
- Each type in
Fs is a cv-unqualified arithmetic type.
F exists.
T is a cv-unqualified floating-point type other than long double.
[Example:
It is implementation-defined whether the constraints
of fadd<bfloat16_t, float32_t> are satisfied.
— end example]
¶
Effects:
All parameters are converted to prvalues of type F.
Each of the function templates has the same behavior as the corresponding
non-template overload with the same name,
if that overload had return type T and parameters all of type F.
¶
Remarks:
The FP_FAST macros also relate to specializations of these function templates.
[Example:
FP_FAST_DSQRTL relates to dsqrtl and fsqrt<double, long double>.
— end example]
5. Acknowledgements
Thanks to Hubert Tong for reviewing the library wording
and pointing out asymmetries with the behavior of <tgmath.h> macros.
6. References