From c678151bdeb322e9c8386dd8d0170380dd3c05f4 Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Sun, 2 Apr 2023 16:31:28 -0500 Subject: [PATCH] Add tests and documentation for fma(). --- ORCACDefs/math.h | 3 + ORCACDefs/tgmath.h | 11 +++ Tests/Conformance/c99math.c | 41 ++++++++++- Tests/Conformance/c99tgmath.c | 132 ++++++++++++++++++++++++++++++++++ cc.notes | 6 ++ 5 files changed, 192 insertions(+), 1 deletion(-) diff --git a/ORCACDefs/math.h b/ORCACDefs/math.h index c4c5a54..271e4b4 100644 --- a/ORCACDefs/math.h +++ b/ORCACDefs/math.h @@ -126,6 +126,9 @@ long double fdiml(long double, long double); double floor(double); float floorf(float); long double floorl(long double); +double fma(double, double, double); +float fmaf(float, float, float); +long double fmal(long double, long double, long double); double fmax(double, double); float fmaxf(float, float); long double fmaxl(long double, long double); diff --git a/ORCACDefs/tgmath.h b/ORCACDefs/tgmath.h index ad2160b..b2e5805 100644 --- a/ORCACDefs/tgmath.h +++ b/ORCACDefs/tgmath.h @@ -32,6 +32,16 @@ long double: fn##l, \ default: _Generic((y), long double: fn##l, default: fn))((x),(y),(other)) +#define __tg_real_x_y_z(fn,x,y,z) _Generic((x), \ + float: _Generic((y), \ + float: _Generic((z), float: fn##f, long double: fn##l, default: fn), \ + long double: fn##l, \ + default: _Generic((z), long double: fn##l, default: fn)), \ + long double: fn##l, \ + default: _Generic((y), \ + long double: fn##l, \ + default: _Generic((z), long double: fn##l, default: fn)))((x),(y),(z)) + #define __tg_x(fn,x) __tg_real_x(fn,(x)) #define __tg_x_y(fn,x,y) __tg_real_x_y(fn,(x),(y)) @@ -54,6 +64,7 @@ #define expm1(x) __tg_real_x(expm1,(x)) #define fabs(x) __tg_real_x(fabs,(x)) #define fdim(x,y) __tg_real_x_y(fdim,(x),(y)) +#define fma(x,y,z) __tg_real_x_y_z(fma,(x),(y),(z)) #define fmax(x,y) __tg_real_x_y(fmax,(x),(y)) #define fmin(x,y) __tg_real_x_y(fmin,(x),(y)) #define floor(x) __tg_real_x(floor,(x)) diff --git a/Tests/Conformance/c99math.c b/Tests/Conformance/c99math.c index 0c5f682..63c8d9f 100644 --- a/Tests/Conformance/c99math.c +++ b/Tests/Conformance/c99math.c @@ -48,6 +48,14 @@ int main(void) { goto Fail; \ } while (0) +#define expect_underflow(op, val) do { \ + feclearexcept(FE_ALL_EXCEPT); \ + if ((op) != (val)) \ + goto Fail; \ + if (!fetestexcept(FE_UNDERFLOW)) \ + goto Fail; \ + } while (0) + #define expect_exact(op, val) \ if ((op) != (val)) \ goto Fail @@ -361,7 +369,7 @@ int main(void) { expect_pole_error(tgammaf(-0.0), -INFINITY); expect_domain_error(tgammal(-2.0)); expect_domain_error(tgammal(-15.0)); - expect_domain_error(tgammal(-1e4900)); + expect_domain_error(tgammal(-1e4900L)); expect_domain_error(tgammal(-INFINITY)); expect_exact(tgammal(+INFINITY),+INFINITY); expect_approx(tgammal(1.0), 1.0); @@ -565,6 +573,37 @@ int main(void) { expect_exact(fminl(-50.0, NAN), -50.0); expect_exact(fminl(NAN, 1e30L), 1e30L); + expect_nan(fma(+INFINITY, +0.0, NAN)); + expect_nan(fmaf(-0.0, -INFINITY, NAN)); + expect_domain_error(fmal(-INFINITY, +0.0, 123.0)); + expect_domain_error(fma(-0.0, +INFINITY, -INFINITY)); + expect_domain_error(fmaf(1e-10, -INFINITY, +INFINITY)); + expect_domain_error(fmal(+INFINITY, 1e-4950L, -INFINITY)); + expect_exact(fma(2.0, 3.0, 5.0), 11.0); + expect_exact(fmal(2e50L, -3.0L, 5e50L), -1e50L); + expect_exact(fmaf(-2.0, -3.0, -7.5), -1.5); + expect_nan(fma(NAN, 1.23, 4.56)); + expect_nan(fmaf(1.23, NAN, 4.56)); + expect_nan(fmal(1.23, 4.56, NAN)); + expect_exact(fmal(+INFINITY, LDBL_TRUE_MIN, -1e4932L), +INFINITY); + expect_overflow(fmal(1e4000L, 1e1000L, -1e4932L), +INFINITY); + expect_exact(fmal(LDBL_MAX, 1.0, LDBL_TRUE_MIN), LDBL_MAX); + expect_exact(fmal(-LDBL_MAX, 1.0, -LDBL_TRUE_MIN), -LDBL_MAX); + fesetround(FE_UPWARD); + expect_overflow(fmal(LDBL_MAX, 1.0, LDBL_TRUE_MIN), +INFINITY); + expect_exact(fmal(-LDBL_MAX, 1.0, -LDBL_TRUE_MIN), -LDBL_MAX); + fesetround(FE_DOWNWARD); + expect_exact(fmal(LDBL_MAX, 1.0, LDBL_TRUE_MIN), LDBL_MAX); + expect_overflow(fmal(-LDBL_MAX, 1.0, -LDBL_TRUE_MIN), -INFINITY); + fesetround(FE_TOWARDZERO); + expect_exact(fmal(LDBL_MAX, 1.0, LDBL_TRUE_MIN), LDBL_MAX); + expect_exact(fmal(-LDBL_MAX, 1.0, -LDBL_TRUE_MIN), -LDBL_MAX); + expect_underflow(fmal(-LDBL_TRUE_MIN, LDBL_TRUE_MIN, LDBL_TRUE_MIN), 0.0); + fesetenv(FE_DFL_ENV); + expect_exact(fmal(-LDBL_TRUE_MIN, LDBL_TRUE_MIN, LDBL_TRUE_MIN), LDBL_TRUE_MIN); + expect_underflow(fmal(-LDBL_TRUE_MIN, LDBL_TRUE_MIN, 0.0), -0.0); + expect_exact(fmal(LDBL_TRUE_MIN, 5.0, -LDBL_TRUE_MIN), LDBL_TRUE_MIN*4.0L); + expect_exact(strtod("-1.25e+3x", &p), -1250.0); expect_exact(*p, 'x'); expect_exact(strtold("-InFin", &p), -INFINITY); expect_exact(*p, 'i'); expect_exact(strtof("INFiniTy", &p), INFINITY); //expect_exact(*p, 0); diff --git a/Tests/Conformance/c99tgmath.c b/Tests/Conformance/c99tgmath.c index 7548359..25f5dfd 100644 --- a/Tests/Conformance/c99tgmath.c +++ b/Tests/Conformance/c99tgmath.c @@ -77,6 +77,138 @@ int main(void) { if (sizeof(nextafter(1LL, 20LL)) != sizeof(double)) goto Fail; + if (sizeof(fma(1.0F, 1.0F, 1.0F)) != sizeof(float)) + goto Fail; + if (sizeof(fma(1.0F, 1.0F, 1.0)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0F, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0F, 1LL)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0, 1.0F)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0, 1.0)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0, 1LL)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0L, 1.0F)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0L, 1.0)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0L, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0F, 1.0L, 1LL)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0F, 1L, 1.0F)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0F, 1L, 1.0)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0F, 1L, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0F, 1L, 1LL)) != sizeof(double)) + goto Fail; + + if (sizeof(fma(1.0, 1.0F, 1.0F)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0, 1.0F, 1.0)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0, 1.0F, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0, 1.0F, 1LL)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0, 1.0, 1.0F)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0, 1.0, 1.0)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0, 1.0, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0, 1.0, 1LL)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0, 1.0L, 1.0F)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0, 1.0L, 1.0)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0, 1.0L, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0, 1.0L, 1LL)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0, 1L, 1.0F)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0, 1L, 1.0)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1.0, 1L, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0, 1L, 1LL)) != sizeof(double)) + goto Fail; + + if (sizeof(fma(1.0L, 1.0F, 1.0F)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0F, 1.0)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0F, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0F, 1LL)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0, 1.0F)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0, 1.0)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0, 1LL)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0L, 1.0F)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0L, 1.0)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0L, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1.0L, 1LL)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1L, 1.0F)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1L, 1.0)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1L, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1.0L, 1L, 1LL)) != sizeof(long double)) + goto Fail; + + if (sizeof(fma(1LL, 1.0F, 1.0F)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1LL, 1.0F, 1.0)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1LL, 1.0F, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1LL, 1.0F, 1LL)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1LL, 1.0, 1.0F)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1LL, 1.0, 1.0)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1LL, 1.0, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1LL, 1.0, 1LL)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1LL, 1.0L, 1.0F)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1LL, 1.0L, 1.0)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1LL, 1.0L, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1LL, 1.0L, 1LL)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1LL, 1L, 1.0F)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1LL, 1L, 1.0)) != sizeof(double)) + goto Fail; + if (sizeof(fma(1LL, 1L, 1.0L)) != sizeof(long double)) + goto Fail; + if (sizeof(fma(1LL, 1L, 1LL)) != sizeof(double)) + goto Fail; + printf ("Passed Conformance Test c99tgmath\n"); return 0; diff --git a/cc.notes b/cc.notes index 7b5b14d..7c0a4ab 100644 --- a/cc.notes +++ b/cc.notes @@ -1218,6 +1218,12 @@ long double fdiml(long double x, long double y); These functions return x - y if x > y, or +0.0 if x <= y. +double fma(double x, double y, double z); +float fmaf(float x, float y, float z); +long double fmal(long double x, long double y, long double z); + +These functions compute (x * y) + z, rounded as one ternary operation. That is, they behave as if the mathematical result is computed exactly and then rounded once to produce the return value. + double fmax(double x, double y); float fmaxf(float x, float y); long double fmaxl(long double x, long double y);