Skip to content

Commit 7329ada

Browse files
committed
C library: implement fma{,f,l}
Model fused multiply-add as documented in its man page.
1 parent 2a12580 commit 7329ada

File tree

7 files changed

+189
-0
lines changed

7 files changed

+189
-0
lines changed

regression/cbmc-library/fma-01/main.c

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <assert.h>
2+
#include <math.h>
3+
4+
int main()
5+
{
6+
double five = fma(1.0, 2.0, 3.0);
7+
assert(five > 4.99 && five < 5.01);
8+
return 0;
9+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--float-overflow-check --nan-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <assert.h>
2+
#include <math.h>
3+
4+
int main()
5+
{
6+
float five = fmaf(1.0f, 2.0f, 3.0f);
7+
assert(five > 4.99f && five < 5.01f);
8+
return 0;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--float-overflow-check --nan-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <assert.h>
2+
#include <math.h>
3+
4+
int main()
5+
{
6+
long double five = fmal(1.0l, 2.0l, 3.0l);
7+
assert(five > 4.99l && five < 5.01l);
8+
return 0;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--float-overflow-check --nan-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring

src/ansi-c/library/math.c

+138
Original file line numberDiff line numberDiff line change
@@ -3599,3 +3599,141 @@ long double powl(long double x, long double y)
35993599
return result_u.l;
36003600
#endif
36013601
}
3602+
3603+
/* FUNCTION: fma */
3604+
3605+
#ifndef __CPROVER_MATH_H_INCLUDED
3606+
# include <math.h>
3607+
# define __CPROVER_MATH_H_INCLUDED
3608+
#endif
3609+
3610+
#ifndef __CPROVER_FENV_H_INCLUDED
3611+
# include <fenv.h>
3612+
# define __CPROVER_FENV_H_INCLUDED
3613+
#endif
3614+
3615+
double __builtin_inf(void);
3616+
3617+
double fma(double x, double y, double z)
3618+
{
3619+
// see man fma (https://linux.die.net/man/3/fma)
3620+
if(isnan(x) || isnan(y))
3621+
return 0.0 / 0.0;
3622+
else if(
3623+
(isinf(x) || isinf(y)) &&
3624+
(fpclassify(x) == FP_ZERO || fpclassify(y) == FP_ZERO))
3625+
{
3626+
feraiseexcept(FE_INVALID);
3627+
return 0.0 / 0.0;
3628+
}
3629+
else if(isnan(z))
3630+
return 0.0 / 0.0;
3631+
3632+
double x_times_y = x * y;
3633+
if(
3634+
isinf(x_times_y) && isinf(z) &&
3635+
__CPROVER_signd(x_times_y) != __CPROVER_signd(z))
3636+
{
3637+
feraiseexcept(FE_INVALID);
3638+
return 0.0 / 0.0;
3639+
}
3640+
3641+
// TODO: detect overflow (FE_OVERFLOW), return +/- __builtin_inf()
3642+
// TODO: detect underflow (FE_UNDERFLOW), return +/- 0
3643+
return x_times_y + z;
3644+
}
3645+
3646+
/* FUNCTION: fmaf */
3647+
3648+
#ifndef __CPROVER_MATH_H_INCLUDED
3649+
# include <math.h>
3650+
# define __CPROVER_MATH_H_INCLUDED
3651+
#endif
3652+
3653+
#ifndef __CPROVER_FENV_H_INCLUDED
3654+
# include <fenv.h>
3655+
# define __CPROVER_FENV_H_INCLUDED
3656+
#endif
3657+
3658+
float __builtin_inff(void);
3659+
3660+
float fmaf(float x, float y, float z)
3661+
{
3662+
// see man fma (https://linux.die.net/man/3/fma)
3663+
if(isnanf(x) || isnanf(y))
3664+
return 0.0f / 0.0f;
3665+
else if(
3666+
(isinff(x) || isinff(y)) &&
3667+
(fpclassify(x) == FP_ZERO || fpclassify(y) == FP_ZERO))
3668+
{
3669+
feraiseexcept(FE_INVALID);
3670+
return 0.0f / 0.0f;
3671+
}
3672+
else if(isnanf(z))
3673+
return 0.0f / 0.0f;
3674+
3675+
float x_times_y = x * y;
3676+
if(
3677+
isinff(x_times_y) && isinff(z) &&
3678+
__CPROVER_signf(x_times_y) != __CPROVER_signf(z))
3679+
{
3680+
feraiseexcept(FE_INVALID);
3681+
return 0.0f / 0.0f;
3682+
}
3683+
3684+
// TODO: detect overflow (FE_OVERFLOW), return +/- __builtin_inff()
3685+
// TODO: detect underflow (FE_UNDERFLOW), return +/- 0
3686+
return x_times_y + z;
3687+
}
3688+
3689+
/* FUNCTION: fmal */
3690+
3691+
#ifndef __CPROVER_MATH_H_INCLUDED
3692+
# include <math.h>
3693+
# define __CPROVER_MATH_H_INCLUDED
3694+
#endif
3695+
3696+
#ifndef __CPROVER_FENV_H_INCLUDED
3697+
# include <fenv.h>
3698+
# define __CPROVER_FENV_H_INCLUDED
3699+
#endif
3700+
3701+
#ifndef __CPROVER_FLOAT_H_INCLUDED
3702+
# include <float.h>
3703+
# define __CPROVER_FLOAT_H_INCLUDED
3704+
#endif
3705+
3706+
long double __builtin_infl(void);
3707+
3708+
long double fmal(long double x, long double y, long double z)
3709+
{
3710+
// see man fma (https://linux.die.net/man/3/fma)
3711+
if(isnanl(x) || isnanl(y))
3712+
return 0.0l / 0.0l;
3713+
else if(
3714+
(isinfl(x) || isinfl(y)) &&
3715+
(fpclassify(x) == FP_ZERO || fpclassify(y) == FP_ZERO))
3716+
{
3717+
feraiseexcept(FE_INVALID);
3718+
return 0.0l / 0.0l;
3719+
}
3720+
else if(isnanl(z))
3721+
return 0.0l / 0.0l;
3722+
3723+
long double x_times_y = x * y;
3724+
if(
3725+
isinfl(x_times_y) && isinfl(z) &&
3726+
__CPROVER_signld(x_times_y) != __CPROVER_signld(z))
3727+
{
3728+
feraiseexcept(FE_INVALID);
3729+
return 0.0l / 0.0l;
3730+
}
3731+
3732+
#if LDBL_MAX_EXP == DBL_MAX_EXP
3733+
return fma(x, y, z);
3734+
#else
3735+
// TODO: detect overflow (FE_OVERFLOW), return +/- __builtin_infl()
3736+
// TODO: detect underflow (FE_UNDERFLOW), return +/- 0
3737+
return x_times_y + z;
3738+
#endif
3739+
}

0 commit comments

Comments
 (0)