The Perl and Raku Conference 2025: Greenville, South Carolina - June 27-29 Learn more

/*************************************************
Documentation of symbols defined by Math::MPFR
MPFR_WANT_FLOAT128 : Defined by Makefile.PL if $have_float128 is
set to a true value. $have_float128 can be set
to a true value by either editing the Makefile.PL
appropriately or by specifying F128=1 in the
Makefile.PL's @ARGV.
The quadmath.h header is included if this symbol
is defined.
NOTE: If MPFR_WANT_FLOAT128 is defined, it is
assumed that the mpfr library was built with
__float128 support - ie was configured with the
'--enable-float128' option.
MPFR_WANT_FLOAT128 must NOT be defined if the
mpfr library has NOT been built with __float128
support.
MPFR_WANT_FLOAT128 does not imply that nvtype is
__float128 - perhaps we have defined
MPFR_WANT_FLOAT128 solely because we wish to make
use of the Math::Float128-Math::MPFR interface.
CAN_PASS_FLOAT128 : Defined only when both MPFR_WANT_FLOAT128 and
USE_QUADMATH is defined, and then only if the mpfr
library is at version 4.0.0 or later. (There was no
__float128 support in the mpfr library prior to
4.0.0.)
DANGER: The assumption is that if MPFR_WANT_FLOAT128
is defined then the mpfr library has been built
with __float128 support, which may not be the case.
Hopefully the configure probing done by the
Makefile.PL will get it right.
MPFR_WANT_DECIMAL_FLOATS : The symbol needs to be defined (before mpfr.h is
included) in order to enable _Decimal64 and/or
_Decimal128 support.
Hence we define it in the Makefile.PL by setting
$have_decimal64 or setting $have_decimal128 to a true
value.
$have_decimal64 can be forcibly set to a true value by
specifying D64=1 in the Makefile.PL's @ARGV.
And $have_decimal128 can likewise be set to a true value
by specifying D128=1 in the Makefile.PL's @ARGV.
$have_decimal64 must not be set to a true value
if the mpfr library has not been built with
_Decimal64 support.
And $have_decimal128 must not be set to a true value
if the mpfr library has not been built with
_Decimal128 support.
We define the symbol solely to enable a Math::MPFR
interface with Math::Decimal64 and/or Math::Decimal128.
Otherwise there's no point (apparent to me) in defining
it.
MPFR_WANT_DECIMAL64 : Defined by the Makefile.PL only if support for the
_Decimal64 type in the mpfr library is detected.
Can be forcibly defined by specifying D64=1 in the
Makefile.PL's @ARGV.
Can be forcibly not defined by specifying D64=0
in the Makefile.PL's @ARGV.
We define the symbol solely to enable a
Math::Decimal64-Math::MPFR interface.
MPFR_WANT_DECIMAL128 : Defined by the Makefile.PL only if support for the
_Decimal128 type in the mpfr library is detected.
Can be forcibly defined by specifying D128=1 in the
Makefile.PL's @ARGV.
Can be forcibly not defined by specifying D128=0
in the Makefile.PL's @ARGV.
We define the symbol solely to enable a
Math::Decimal128-Math::MPFR interface.
HAVE_IEEE_754_LONG_DOUBLE :Used only by the test suite.
Defined by Makefile.PL if
($Config{longdblkind} == 1 ||
$Config{longdblkind} == 2)
This implies that long double is the quad (128-bit)
long double.
HAVE_EXTENDED_PRECISION_LONG_DOUBLE :
Used only by the test suite.
Defined by Makefile.PL if
($Config{longdblkind} == 3 ||
$Config{longdblkind} == 4)
This implies that nvtype is the extended
precision (80-bit) long double.
REQUIRED_LDBL_MANT_DIG : Defined to float.h's LDBL_MANT_DIG unless
LDBL_MANT_DIG is 106 (ie long double is
double-double) - in which case it is defined to
be 2098.
This is needed to ensure that the mpfr value is
an accurate rendition of the double-double value.
CHECK_ROUNDING_VALUE : Macro that checks (on pre-4.0.0 mpfr versions only)
that the rounding value provided is in the
allowable range of 0-4 inclusive.
(On 2.x.x versions the allowable range is only 0-3,
but we don't support those versions anyway.)
FAILS_CHECK_INPUT_BASE : Macro that checks that the base (where specified)
is in the accepted range.
FAILS_CHECK_OUTPUT_BASE : Macro that checks that the base (where specified)
is in the accepted range.
DEAL_WITH_NANFLAG_BUG : Macro that corrects certain failures (in mpfr
versions prior to 3.1.4) to set the NaN flag.
DEAL_WITH_NANFLAG_BUG_OVERLOADED
: Another macro that corrects the same bug as
DEAL_WITH_NANFLAG_BUG - but recoded for the
overloaded operations affected by the bug.
MATH_MPFR_NEED_LONG_LONG_INT
: Defined by Makefile.PL if
$Config{ivsize} >= 8 && $Config{ivtype} is not
'long' && $use_64_bit_int (in the Makefile.PL)
has not been set to -1. This symbol will also be
defined if $use_64_bit_int is set to 1.
The setting of this symbol is taken to imply that
the mpfr _uj/_sj functions are needed for
converting mpfr integer values to perl integers.
Conversely, if the symbol is not defined, then
the implication is that the _uj/sj functions are
not needed (because the _ui/_si functions, which
are alway available) provide the same
functionality) - and therefore those _uj/_sj
functions are then not made available.
IVSIZE_BITS : Defined only if MATH_MPFR_NEED_LONG_LONG_INT is
defined - whereupon it will be set to the bitsize
of the IV (perl's integer type).
Currently, I think this symbol will only ever be
either undefined or set to 64 - and I suspect
that it could (currently) be replaced with a hard
coded 64 wherever it occurs in the code.
NVSIZE_BITS : Defined to the maximum number of bits effectively
representable by the NV. This will be either 53,
64, 113 or 2098 - depending on perl's NV type.
_WIN32_BIZARRE_INFNAN : Defined (on Windows only) when the perl version
(as expressed by $]) is less than 5.022.
These earlier perl versions had bizarre strings
representing NaNs (eg 1.#IND) and Infs (eg 1.#INF)
on Win32.
LD_SUBNORMAL_BUG : Defined for mpfr-3.1.4 and earlier if and only if
LDBL_MANT_DIG == 64
(The bug is in mpfr_get_ld)
FALLBACK_NOTIFY : If defined, $Math::MPFR::doubletoa_fallback
(initially set to 0) will be incremented by 1 on
those rare occasions where grisu3 fails and
falls back to the fallback routine.
For more details, see the doubletoa documentation.
*************************************************/
#include <stdio.h>
#include <inttypes.h>
/*
* In mpfr-4.1.0, the _Float128 type is exposed in mpfr.h if MPFR_WANT_FLOAT128 is defined.
* We fall back to defining it to __float128 if the _Float128 type is unknown.
*/
#if defined(MPFR_WANT_FLOAT128) && defined(__GNUC__) && !defined(__FLT128_MAX__) && !defined(_BITS_FLOATN_H)
#define _Float128 __float128
#endif
#include <gmp.h>
#include <mpfr.h>
#include <float.h>
#if MPFR_VERSION_MAJOR < 3
#define mpfr_exp_t mp_exp_t
#endif
#if defined(MPFR_WANT_FLOAT128) || defined(USE_QUADMATH)
#include <quadmath.h>
#if defined(USE_QUADMATH) && defined(MPFR_WANT_FLOAT128) && defined(MPFR_VERSION) && MPFR_VERSION >= MPFR_VERSION_NUM(4,0,0)
#define CAN_PASS_FLOAT128
#endif
#if defined(__MINGW32__) && !defined(__MINGW64__)
typedef __float128 float128 __attribute__ ((aligned(32)));
#elif defined(__MINGW64__) || (defined(DEBUGGING) && NVSIZE == 8)
typedef __float128 float128 __attribute__ ((aligned(8)));
#else
typedef __float128 float128;
#endif
#endif
#if defined(MPFR_WANT_DECIMAL128)
#if defined(__MINGW64__) || (defined(DEBUGGING) && NVSIZE == 8)
typedef _Decimal128 D128 __attribute__ ((aligned(8)));
#else
typedef _Decimal128 D128;
#endif
#endif
#define SIS_PERL_VERSION PERL_REVISION*1000000+PERL_VERSION*1000+PERL_SUBVERSION
#if SIS_PERL_VERSION >= 5012000 /* perl-5.12.0 and later */
# define MORTALIZED_PV(x) newSVpvn_flags(x,strlen(x),SVs_TEMP)
#else
# define MORTALIZED_PV(x) sv_2mortal(newSVpv(x,0))
#endif
/* Facilitate altering the numeric flags that we *
* look at (in case it becomes necessary) */
#define SV_IS_IOK(x) \
SvIOK(x)
#define SV_IS_POK(x) \
SvPOK(x)
#define SV_IS_NOK(x) \
SvNOK(x)
#if (!defined(MPFR_VERSION) || MPFR_VERSION <= 196868) && LDBL_MANT_DIG == 64
#define LD_SUBNORMAL_BUG 1
#endif
#if LDBL_MANT_DIG == 106
#define REQUIRED_LDBL_MANT_DIG 2098
#else
#define REQUIRED_LDBL_MANT_DIG LDBL_MANT_DIG
#endif
#define FAILS_CHECK_INPUT_BASE \
!SV_IS_IOK(base) || SvIVX(base) < 0 || SvIVX(base) > 62 || SvIVX(base) == 1
#if MPFR_VERSION >= 262400 /* Allowable range of base has been expanded */
#define FAILS_CHECK_OUTPUT_BASE \
!(SV_IS_IOK(base) && ((SvIVX(base) >= 2 && SvIVX(base) <= 62) || (SvIVX(base) >= -36 && SvIVX(base) <= -2)))
#else
#define FAILS_CHECK_OUTPUT_BASE \
!(SV_IS_IOK(base) && SvIVX(base) >= 2 && SvIVX(base) <= 62)
#endif
/* Don't use CHECK_ROUNDING_VALUE macro with Rmpfr_set_NV *
* (as this function's "round" arg is "unsigned int", not SV*) */
#if MPFR_VERSION_MAJOR < 4
#define CHECK_ROUNDING_VALUE \
if((mp_rnd_t)SvUV(round) > 4) \
croak("Illegal rounding value supplied for this version (%s) of the mpfr library", MPFR_VERSION_STRING);
#else
#define CHECK_ROUNDING_VALUE
#endif
#define NOK_POK_DUALVAR_CHECK \
if(SV_IS_NOK(b)) { \
nok_pok++; \
if(SvIV(get_sv("Math::MPFR::NOK_POK", 0))) \
warn("Scalar passed to %s is both NV and PV. Using PV (string) value"
/* Don't use NON_NUMERIC_CHAR_CHECK macro with Rmpfr_inp_str as this *
* function requires a different condition (!ret vs ret). */
#define NON_NUMERIC_CHAR_CHECK(inputstr) \
if(ret) { \
nnum++; \
if(SvIV(get_sv("Math::MPFR::NNW", 0))) \
warn("string used (%s) in %s contains non-numeric characters", SvPV_nolen(inputstr)
#define BITSEARCH_4 \
if(tmp & 8) { \
subnormal_prec_adjustment += 1; \
break; \
} \
if(tmp & 4) { \
subnormal_prec_adjustment += 2; \
break; \
} \
if(tmp & 2) { \
subnormal_prec_adjustment += 3; \
break; \
} \
subnormal_prec_adjustment += 4;
#define BITSEARCH_8 \
if(tmp & 128) { \
subnormal_prec_adjustment += 1; \
break; \
} \
if(tmp & 64) { \
subnormal_prec_adjustment += 2; \
break; \
} \
if(tmp & 32) { \
subnormal_prec_adjustment += 3; \
break; \
} \
if(tmp & 16) { \
subnormal_prec_adjustment += 4; \
break; \
} \
if(tmp & 8) { \
subnormal_prec_adjustment += 5; \
break; \
} \
if(tmp & 4) { \
subnormal_prec_adjustment += 6; \
break; \
} \
if(tmp & 2) { \
subnormal_prec_adjustment += 7; \
break; \
} \
subnormal_prec_adjustment += 8;
#define NEG_ZERO_BUG 196866 /* A bug affecting mpfr_fits_u*_p functions */
/* Fixed in mpfr after MPFR_VERSION 196866 (3.1.2) */
/* For earlier versions of mpfr, we fix this bug in */
/* our own code */
#define LNGAMMA_BUG 196867 /* lngamma(-0) set to NaN instead of +Inf */
/* Fixed in mpfr after MPFR_VERSION 196867 (3.1.3) */
/* For earlier versions of mpfr, we fix this bug in */
/* our own code */
#define NANFLAG_BUG 196868 /* A bug affecting setting of the NaN flag */
/* Fixed in mpfr after MPFR_VERSION 196868 (3.1.4) */
/* For earlier versions of mpfr, we fix this bug in */
/* our own code */
#define DD_INF_BUG 196869 /* mpfr_get_ld on (double-double platforms only) */
/* might return NaN when it sould return Inf. */
/* Presumably, this will be */
/* fixed in mpfr after MPFR_VERSION 196869 (3.1.5) */
/* For earlier versions of mpfr, we fix this bug in */
/* our own code */
#if !defined(MPFR_VERSION) || (defined(MPFR_VERSION) && MPFR_VERSION <= NANFLAG_BUG)
#define DEAL_WITH_NANFLAG_BUG if(mpfr_nan_p(*b))mpfr_set_nanflag();
#define DEAL_WITH_NANFLAG_BUG_OVERLOADED if(mpfr_nan_p(*(INT2PTR(mpfr_t *,SvIVX(SvRV(a))))))mpfr_set_nanflag();
#else
#define DEAL_WITH_NANFLAG_BUG
#define DEAL_WITH_NANFLAG_BUG_OVERLOADED
#endif
/*
#ifdef _MSC_VER
#pragma warning(disable:4700 4715 4716)
#endif
*/
#ifdef OLDPERL
#define SvUOK SvIsUV
#endif
#ifndef Newx
# define Newx(v,n,t) New(0,v,n,t)
#endif
#ifndef Newxz
# define Newxz(v,n,t) Newz(0,v,n,t)
#endif
/* A perl bug in perl-5.20 onwards can break &PL_sv_yes and *
* &PL_sv_no. In the overload subs we therefore instead *
* use SvTRUE_nomg_NN where possible, which is available *
* beginning with perl-5.18.0. *
* Otherwise we continue using &PL_sv_yes as original *
* (&PL_sv_no is not used by this module.) *
#if defined SvTRUE_nomg_NN
#define SWITCH_ARGS SvTRUE_nomg_NN(third)
#define NO_SWITCH_ARGS !SvTRUE_nomg_NN(third)
#else
#define SWITCH_ARGS third==&PL_sv_yes
#define NO_SWITCH_ARGS third!=&PL_sv_yes
#endif
/* May one day be removed from mpfr.h */
#ifndef mp_rnd_t
# define mp_rnd_t mpfr_rnd_t
#endif
#ifndef mp_prec_t
# define mp_prec_t mpfr_prec_t
#endif
#ifndef __gmpfr_default_rounding_mode
#define __gmpfr_default_rounding_mode mpfr_get_default_rounding_mode()
#endif
#if !defined(__GNU_MP_VERSION) || __GNU_MP_VERSION < 5
#define mp_bitcnt_t unsigned long int
#endif
/* For nvtoa() */
#if defined(MPFR_HAVE_BENDIAN) /* big endian architecture - defined by Makefile.PL */
#define INC_OR_DEC(p) p++
#else /* little endian */
#define INC_OR_DEC(p) p--
#endif
#if NVSIZE == 8
#define MATH_MPFR_MAX_DIG 17
#define NVSIZE_BITS 53
#define MATH_MPFR_NV_MAX 0x1.fffffffffffffp+1023 /* 32-bit Windows has a problem with evaluating the *
* decimal form of this value if -std=c99 is specified */
#define MATH_MPFR_NORMAL_MIN 0x1p-1022
# if defined(MPFR_HAVE_BENDIAN) /* big endian architecture - defined by Makefile.PL */
# define D_CONDITION_1(p) p<=7
# define DIND_0 0
# define DIND_1 1
# define DIND_2 2
# else /* little endian architecture */
# define D_CONDITION_1(p) p>=0
# define DIND_0 7
# define DIND_1 6
# define DIND_2 5
# endif
#elif defined(USE_LONG_DOUBLE) && REQUIRED_LDBL_MANT_DIG == 64
#define MATH_MPFR_MAX_DIG 21
#define NVSIZE_BITS 64
#define MATH_MPFR_NV_MAX 0xf.fffffffffffffffp+16380L
#define MATH_MPFR_NORMAL_MIN 0x8p-16385L
# if defined(MPFR_HAVE_BENDIAN) /* big endian architecture - defined by Makefile.PL */
# define LD_CONDITION_1(p) p<=9
# define LDIND_0 0
# define LDIND_1 1
# define LDIND_2 2
# else /* little endian architecture */
# define LD_CONDITION_1(p) p>=0
# define LDIND_0 9
# define LDIND_1 8
# define LDIND_2 7
# endif
#elif defined(USE_LONG_DOUBLE) && REQUIRED_LDBL_MANT_DIG == 2098
#define MATH_MPFR_MAX_DIG 33
#define NVSIZE_BITS 2098
#define MATH_MPFR_NV_MAX 1.797693134862315807937289714053e+308L
#define MATH_MPFR_NORMAL_MIN 2.2250738585072014e-308
#else
#define MATH_MPFR_MAX_DIG 36
#define NVSIZE_BITS 113
#define MATH_MPFR_NV_MAX 0x1.ffffffffffffffffffffffffffffp+16383Q
#define MATH_MPFR_NORMAL_MIN 0x1p-16382Q /* == 0x8p-16385 */
# if defined(MPFR_HAVE_BENDIAN) /* big endian architecture - defined by Makefile.PL */
# define Q_CONDITION_1(p) p<=15
# define QIND_0 0
# define QIND_1 1
# define QIND_2 2
# else /* little endian architecture */
# define Q_CONDITION_1(p) p>=0
# define QIND_0 15
# define QIND_1 14
# define QIND_2 13
# endif
#endif
/* End of defines for nvtoa() */
#define NEW_MATH_MPFR_OBJECT(PACNAME,FUNCNAME) \
Newx(mpfr_t_obj, 1, mpfr_t); \
if(mpfr_t_obj == NULL) croak("Failed to allocate memory in FUNCNAME function"); \
obj_ref = newSV(0); \
obj = newSVrv(obj_ref, PACNAME);
#define OBJ_READONLY_ON \
sv_setiv(obj, INT2PTR(IV,mpfr_t_obj)); \
SvREADONLY_on(obj);
#define RETURN_STACK_2 \
ST(0) = sv_2mortal(obj_ref); \
ST(1) = sv_2mortal(newSViv(ret)); \
XSRETURN(2);