| //! Interfaces needed to support testing with multi-precision floating point numbers. | |
| //! | |
| //! Within this module, the macros create a submodule for each `libm` function. These contain | |
| //! a struct named `Operation` that implements [`MpOp`]. | |
| use std::cmp::Ordering; | |
| use rug::Assign; | |
| use rug::az::{self, Az, CheckedCast, OverflowingCast, WrappingAs}; | |
| use rug::float::Round::Nearest; | |
| use rug::ops::{ | |
| AddAssignRound, DivAssignRound, MulAssignRound, PowAssignRound, RemAssignRound, SubAssignRound, | |
| }; | |
| pub use rug::{Float as MpFloat, Integer as MpInt}; | |
| use crate::{Arg0, Arg1, Arg2, Float, MathOp, Ret0, Ret1}; | |
| /// Create a multiple-precision float with the correct number of bits for a concrete float type. | |
| fn new_mpfloat<F: Float>() -> MpFloat { | |
| MpFloat::new(F::SIG_BITS + 1) | |
| } | |
| /// Set subnormal emulation and convert to a concrete float type. | |
| fn prep_retval<F>(mp: &mut MpFloat, ord: Ordering) -> F | |
| where | |
| for<'a> &'a MpFloat: az::Cast<F>, | |
| { | |
| mp.subnormalize_ieee_round(ord, Nearest); | |
| (&*mp).az::<F>() | |
| } | |
| /// Structures that represent a float operation. | |
| /// | |
| pub trait MpOp: MathOp { | |
| /// The struct itself should hold any context that can be reused among calls to `run` (allocated | |
| /// `MpFloat`s). | |
| type MpTy; | |
| /// Create a new instance. | |
| fn new_mp() -> Self::MpTy; | |
| /// Perform the operation. | |
| /// | |
| /// Usually this means assigning inputs to cached floats, performing the operation, applying | |
| /// subnormal approximation, and converting the result back to concrete values. | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet; | |
| } | |
| /// Implement `MpOp` for functions with a single return value. | |
| macro_rules! impl_mp_op { | |
| // Matcher for unary functions | |
| ( | |
| fn_name: $fn_name:ident, | |
| RustFn: fn($_fty:ty,) -> $_ret:ty, | |
| attrs: [$($attr:meta),*], | |
| fn_extra: $fn_name_normalized:expr, | |
| ) => { | |
| paste::paste! { | |
| $(#[$attr])* | |
| impl MpOp for crate::op::$fn_name::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| let ord = this.[< $fn_name_normalized _round >](Nearest); | |
| prep_retval::<Self::RustRet>(this, ord) | |
| } | |
| } | |
| } | |
| }; | |
| // Matcher for binary functions | |
| ( | |
| fn_name: $fn_name:ident, | |
| RustFn: fn($_fty:ty, $_fty2:ty,) -> $_ret:ty, | |
| attrs: [$($attr:meta),*], | |
| fn_extra: $fn_name_normalized:expr, | |
| ) => { | |
| paste::paste! { | |
| $(#[$attr])* | |
| impl MpOp for crate::op::$fn_name::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg0<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let ord = this.0.[< $fn_name_normalized _round >](&this.1, Nearest); | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| } | |
| }; | |
| // Matcher for ternary functions | |
| ( | |
| fn_name: $fn_name:ident, | |
| RustFn: fn($_fty:ty, $_fty2:ty, $_fty3:ty,) -> $_ret:ty, | |
| attrs: [$($attr:meta),*], | |
| fn_extra: $fn_name_normalized:expr, | |
| ) => { | |
| paste::paste! { | |
| $(#[$attr])* | |
| impl MpOp for crate::op::$fn_name::Routine { | |
| type MpTy = (MpFloat, MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| ( | |
| new_mpfloat::<Arg0<Self>>(), | |
| new_mpfloat::<Arg1<Self>>(), | |
| new_mpfloat::<Arg2<Self>>(), | |
| ) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.2.assign(input.2); | |
| let ord = this.0.[< $fn_name_normalized _round >](&this.1, &this.2, Nearest); | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| libm_macros::for_each_function! { | |
| callback: impl_mp_op, | |
| emit_types: [RustFn], | |
| skip: [ | |
| // Most of these need a manual implementation | |
| // verify-sorted-start | |
| addf128, | |
| addf16, | |
| addf32, | |
| addf64, | |
| ashl_u128, | |
| ashl_u32, | |
| ashl_u64, | |
| ashr_i128, | |
| ashr_i32, | |
| ashr_i64, | |
| ceil, | |
| ceilf, | |
| ceilf128, | |
| ceilf16, | |
| copysign, | |
| copysignf, | |
| copysignf128, | |
| copysignf16, | |
| divf128, | |
| divf32, | |
| divf64, | |
| eqf128, | |
| eqf16, | |
| eqf32, | |
| eqf64, | |
| extend_f16_f128, | |
| extend_f16_f32, | |
| extend_f16_f64, | |
| extend_f32_f128, | |
| extend_f32_f64, | |
| extend_f64_f128, | |
| fabs, | |
| fabsf, | |
| fabsf128, | |
| fabsf16,floor, | |
| floorf, | |
| floorf128, | |
| floorf16, | |
| fmaximum, | |
| fmaximumf, | |
| fmaximumf128, | |
| fmaximumf16, | |
| fminimum, | |
| fminimumf, | |
| fminimumf128, | |
| fminimumf16, | |
| fmod, | |
| fmodf, | |
| fmodf128, | |
| fmodf16, | |
| frexp, | |
| frexpf, | |
| frexpf128, | |
| frexpf16, | |
| ftoi_f128_i128, | |
| ftoi_f128_i32, | |
| ftoi_f128_i64, | |
| ftoi_f128_u128, | |
| ftoi_f128_u32, | |
| ftoi_f128_u64, | |
| ftoi_f32_i128, | |
| ftoi_f32_i32, | |
| ftoi_f32_i64, | |
| ftoi_f32_u128, | |
| ftoi_f32_u32, | |
| ftoi_f32_u64, | |
| ftoi_f64_i128, | |
| ftoi_f64_i32, | |
| ftoi_f64_i64, | |
| ftoi_f64_u128, | |
| ftoi_f64_u32, | |
| ftoi_f64_u64, | |
| gef128, | |
| gef16, | |
| gef32, | |
| gef64, | |
| gtf128, | |
| gtf16, | |
| gtf32, | |
| gtf64, | |
| iadd_i128, | |
| iadd_u128, | |
| iaddo_i128, | |
| iaddo_u128, | |
| idiv_i128, | |
| idiv_i32, | |
| idiv_i64, | |
| idiv_u128, | |
| idiv_u32, | |
| idiv_u64, | |
| idivmod_i128, | |
| idivmod_i32, | |
| idivmod_i64, | |
| idivmod_u128, | |
| idivmod_u32, | |
| idivmod_u64, | |
| ilogb, | |
| ilogbf, | |
| ilogbf128, | |
| ilogbf16, | |
| imod_i128, | |
| imod_i32, | |
| imod_i64, | |
| imod_u128, | |
| imod_u32, | |
| imod_u64, | |
| imul_i128, | |
| imul_u64, | |
| imulo_i128, | |
| imulo_i32, | |
| imulo_i64, | |
| imulo_u128, | |
| isub_i128, | |
| isub_u128, | |
| isubo_i128, | |
| isubo_u128, | |
| itof_i128_f128, | |
| itof_i128_f32, | |
| itof_i128_f64, | |
| itof_i32_f128, | |
| itof_i32_f32, | |
| itof_i32_f64, | |
| itof_i64_f128, | |
| itof_i64_f32, | |
| itof_i64_f64, | |
| itof_u128_f128, | |
| itof_u128_f32, | |
| itof_u128_f64, | |
| itof_u32_f128, | |
| itof_u32_f32, | |
| itof_u32_f64, | |
| itof_u64_f128, | |
| itof_u64_f32, | |
| itof_u64_f64, | |
| jn, | |
| jnf, | |
| ldexp, | |
| ldexpf, | |
| ldexpf128, | |
| ldexpf16, | |
| leading_zeros_u128, | |
| leading_zeros_u32, | |
| leading_zeros_u64, | |
| lef128, | |
| lef16, | |
| lef32, | |
| lef64, | |
| lgamma, | |
| lgamma_r, | |
| lgammaf, | |
| lgammaf_r, | |
| lshr_u128, | |
| lshr_u32, | |
| lshr_u64, | |
| ltf128, | |
| ltf16, | |
| ltf32, | |
| ltf64, | |
| modf, | |
| modff, | |
| mulf128, | |
| mulf16, | |
| mulf32, | |
| mulf64, | |
| narrow_f128_f16, | |
| narrow_f128_f32, | |
| narrow_f128_f64, | |
| narrow_f32_f16, | |
| narrow_f64_f16, | |
| narrow_f64_f32, | |
| nef128, | |
| nef16, | |
| nef32, | |
| nef64, | |
| nextafter, | |
| nextafterf, | |
| pow, | |
| powf,remquo, | |
| powif128, | |
| powif32, | |
| powif64, | |
| remquof, | |
| rint, | |
| rintf, | |
| rintf128, | |
| rintf16, | |
| round, | |
| roundeven, | |
| roundevenf, | |
| roundevenf128, | |
| roundevenf16, | |
| roundf, | |
| roundf128, | |
| roundf16, | |
| scalbn, | |
| scalbnf, | |
| scalbnf128, | |
| scalbnf16, | |
| sincos,sincosf, | |
| subf128, | |
| subf16, | |
| subf32, | |
| subf64, | |
| trailing_zeros_u128, | |
| trailing_zeros_u32, | |
| trailing_zeros_u64, | |
| trunc, | |
| truncf, | |
| truncf128, | |
| truncf16,yn, | |
| unordf128, | |
| unordf16, | |
| unordf32, | |
| unordf64, | |
| ynf, | |
| // verify-sorted-end | |
| ], | |
| fn_extra: match MACRO_FN_NAME { | |
| // Remap function names that are different between mpfr and libm | |
| expm1 | expm1f => exp_m1, | |
| fabs | fabsf => abs, | |
| fdim | fdimf | fdimf16 | fdimf128 => positive_diff, | |
| fmaf16 | fma | fmaf | fmaf128 => mul_add, | |
| fmax | fmaxf | fmaxf16 | fmaxf128 | | |
| fmaximum_num | fmaximum_numf | fmaximum_numf16 | fmaximum_numf128 => max, | |
| fmin | fminf | fminf16 | fminf128 | | |
| fminimum_num | fminimum_numf | fminimum_numf16 | fminimum_numf128 => min, | |
| log | logf => ln, | |
| log1p | log1pf => ln_1p, | |
| tgamma | tgammaf => gamma, | |
| _ => MACRO_FN_NAME_NORMALIZED | |
| } | |
| } | |
| /// Implement unary functions that don't have a `_round` version | |
| macro_rules! impl_no_round { | |
| // Unary matcher | |
| ($($fn_name:ident => $rug_name:ident;)*) => { | |
| paste::paste! { | |
| $( impl_no_round!{ @inner_unary $fn_name, $rug_name } )* | |
| } | |
| }; | |
| (@inner_unary $fn_name:ident, $rug_name:ident) => { | |
| impl MpOp for crate::op::$fn_name::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| this.$rug_name(); | |
| prep_retval::<Self::RustRet>(this, Ordering::Equal) | |
| } | |
| } | |
| }; | |
| } | |
| impl_no_round! { | |
| ceil => ceil_mut; | |
| ceilf => ceil_mut; | |
| fabs => abs_mut; | |
| fabsf => abs_mut; | |
| floor => floor_mut; | |
| floorf => floor_mut; | |
| rint => round_even_mut; // FIXME: respect rounding mode | |
| rintf => round_even_mut; // FIXME: respect rounding mode | |
| round => round_mut; | |
| roundeven => round_even_mut; | |
| roundevenf => round_even_mut; | |
| roundf => round_mut; | |
| trunc => trunc_mut; | |
| truncf => trunc_mut; | |
| } | |
| #[cfg(f16_enabled)] | |
| impl_no_round! { | |
| ceilf16 => ceil_mut; | |
| fabsf16 => abs_mut; | |
| floorf16 => floor_mut; | |
| rintf16 => round_even_mut; // FIXME: respect rounding mode | |
| roundf16 => round_mut; | |
| roundevenf16 => round_even_mut; | |
| truncf16 => trunc_mut; | |
| } | |
| #[cfg(f128_enabled)] | |
| impl_no_round! { | |
| ceilf128 => ceil_mut; | |
| fabsf128 => abs_mut; | |
| floorf128 => floor_mut; | |
| rintf128 => round_even_mut; // FIXME: respect rounding mode | |
| roundf128 => round_mut; | |
| roundevenf128 => round_even_mut; | |
| truncf128 => trunc_mut; | |
| } | |
| /// Some functions are difficult to do in a generic way. Implement them here. | |
| macro_rules! impl_op_for_ty { | |
| ($fty:ty, $suffix:literal) => { | |
| paste::paste! { | |
| impl MpOp for crate::op::[<modf $suffix>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Ret0<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(&this.0); | |
| let (ord0, ord1) = this.0.trunc_fract_round(&mut this.1, Nearest); | |
| ( | |
| prep_retval::<Ret0<Self>>(&mut this.1, ord0), | |
| prep_retval::<Ret1<Self>>(&mut this.0, ord1), | |
| ) | |
| } | |
| } | |
| impl MpOp for crate::op::[<pow $suffix>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let ord = this.0.pow_assign_round(&this.1, Nearest); | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| impl MpOp for crate::op::[<jn $suffix>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg1<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| let (n, x) = input; | |
| this.assign(x); | |
| let ord = this.jn_round(n, Nearest); | |
| prep_retval::<Self::RustRet>(this, ord) | |
| } | |
| } | |
| impl MpOp for crate::op::[<sincos $suffix>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Ret0<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(0.0); | |
| let (sord, cord) = this.0.sin_cos_round(&mut this.1, Nearest); | |
| ( | |
| prep_retval::<Ret0<Self>>(&mut this.0, sord), | |
| prep_retval::<Ret1<Self>>(&mut this.1, cord) | |
| ) | |
| } | |
| } | |
| impl MpOp for crate::op::[<remquo $suffix>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| ( | |
| new_mpfloat::<Arg0<Self>>(), | |
| new_mpfloat::<Arg1<Self>>(), | |
| ) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let (ord, q) = this.0.remainder_quo31_round(&this.1, Nearest); | |
| (prep_retval::<Ret0<Self>>(&mut this.0, ord), q) | |
| } | |
| } | |
| impl MpOp for crate::op::[<yn $suffix>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg1<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| let (n, x) = input; | |
| this.assign(x); | |
| let ord = this.yn_round(n, Nearest); | |
| prep_retval::<Self::RustRet>(this, ord) | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| /// Version of `impl_op_for_ty` with only functions that have `f16` and `f128` implementations. | |
| macro_rules! impl_op_for_ty_all { | |
| ($fty:ty, $suffix:literal) => { | |
| paste::paste! { | |
| impl MpOp for crate::op::[<add $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let ord = this.0.add_assign_round(&this.1, Nearest); | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| impl MpOp for crate::op::[<sub $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let ord = this.0.sub_assign_round(&this.1, Nearest); | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| impl MpOp for crate::op::[<mul $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let ord = this.0.mul_assign_round(&this.1, Nearest); | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| impl MpOp for crate::op::[<eq $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.0 == this.1 | |
| } | |
| } | |
| impl MpOp for crate::op::[<gt $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.0 > this.1 | |
| } | |
| } | |
| impl MpOp for crate::op::[<ge $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.0 >= this.1 | |
| } | |
| } | |
| impl MpOp for crate::op::[<lt $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.0 < this.1 | |
| } | |
| } | |
| impl MpOp for crate::op::[<le $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.0 <= this.1 | |
| } | |
| } | |
| impl MpOp for crate::op::[<ne $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.0 != this.1 | |
| } | |
| } | |
| impl MpOp for crate::op::[<unord $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.0.is_nan() || this.1.is_nan() | |
| } | |
| } | |
| impl MpOp for crate::op::[<copysign $suffix>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.0.copysign_mut(&this.1); | |
| prep_retval::<Self::RustRet>(&mut this.0, Ordering::Equal) | |
| } | |
| } | |
| impl MpOp for crate::op::[<fmod $suffix>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let ord = this.0.rem_assign_round(&this.1, Nearest); | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| impl MpOp for crate::op::[< fmaximum $suffix >]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let ord = if this.0.is_nan() || this.1.is_nan() { | |
| this.0.assign($fty::NAN); | |
| Ordering::Equal | |
| } else { | |
| this.0.max_round(&this.1, Nearest) | |
| }; | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| impl MpOp for crate::op::[< fminimum $suffix >]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let ord = if this.0.is_nan() || this.1.is_nan() { | |
| this.0.assign($fty::NAN); | |
| Ordering::Equal | |
| } else { | |
| this.0.min_round(&this.1, Nearest) | |
| }; | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| impl MpOp for crate::op::[<frexp $suffix>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| let exp = this.frexp_mut(); | |
| (prep_retval::<Ret0<Self>>(this, Ordering::Equal), exp) | |
| } | |
| } | |
| impl MpOp for crate::op::[<ilogb $suffix>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| // `get_exp` follows `frexp` for `0.5 <= |m| < 1.0`. Adjust the exponent by | |
| // one to scale the significand to `1.0 <= |m| < 2.0`. | |
| this.get_exp().map(|v| v - 1).unwrap_or_else(|| { | |
| if this.is_infinite() { | |
| i32::MAX | |
| } else { | |
| // Zero or NaN | |
| i32::MIN | |
| } | |
| }) | |
| } | |
| } | |
| // `ldexp` and `scalbn` are the same for binary floating point, so just forward all | |
| // methods. | |
| impl MpOp for crate::op::[<ldexp $suffix>]::Routine { | |
| type MpTy = <crate::op::[<scalbn $suffix>]::Routine as MpOp>::MpTy; | |
| fn new_mp() -> Self::MpTy { | |
| <crate::op::[<scalbn $suffix>]::Routine as MpOp>::new_mp() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| <crate::op::[<scalbn $suffix>]::Routine as MpOp>::run(this, input) | |
| } | |
| } | |
| impl MpOp for crate::op::[<scalbn $suffix>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| *this <<= input.1; | |
| prep_retval::<Self::RustRet>(this, Ordering::Equal) | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| macro_rules! impl_op_for_ty_no_f16 { | |
| ($fty:ty, $suffix:literal) => { | |
| paste::paste! { | |
| impl MpOp for crate::op::[<div $fty>]::Routine { | |
| type MpTy = (MpFloat, MpFloat); | |
| fn new_mp() -> Self::MpTy { | |
| (new_mpfloat::<Arg0<Self>>(), new_mpfloat::<Arg1<Self>>()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| let ord = this.0.div_assign_round(&this.1, Nearest); | |
| prep_retval::<Self::RustRet>(&mut this.0, ord) | |
| } | |
| } | |
| impl MpOp for crate::op::[<powi $fty>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| let ord = this.pow_assign_round(input.1, Nearest); | |
| prep_retval::<Self::RustRet>(this, ord) | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| macro_rules! impl_extend_trunc { | |
| ($narrow:ty, $wide:ty) => { | |
| paste::paste! { | |
| impl MpOp for crate::op::[<extend_ $narrow _ $wide>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| prep_retval::<Self::RustRet>(this, Ordering::Equal) | |
| } | |
| } | |
| impl MpOp for crate::op::[<narrow_ $wide _ $narrow>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| prep_retval::<Self::RustRet>(this, Ordering::Equal) | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| macro_rules! impl_ftoi_itof { | |
| ($fty:ty, $ity:ty) => { | |
| paste::paste! { | |
| impl MpOp for crate::op::[<ftoi_ $fty _ $ity>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| this.trunc_mut(); | |
| this.subnormalize_ieee_round(Ordering::Equal, Nearest); | |
| (&*this).checked_cast().unwrap_or_else(|| { | |
| if this.is_nan() { | |
| 0 | |
| } else if this.is_sign_negative() { | |
| Self::RustRet::MIN | |
| } else { | |
| Self::RustRet::MAX | |
| } | |
| }) | |
| } | |
| } | |
| impl MpOp for crate::op::[<itof_ $ity _ $fty>]::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Self::RustRet>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| prep_retval::<Self::RustRet>(this, Ordering::Equal) | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| impl_op_for_ty!(f32, "f"); | |
| impl_op_for_ty!(f64, ""); | |
| impl_op_for_ty_no_f16!(f32, "f"); | |
| impl_op_for_ty_no_f16!(f64, ""); | |
| #[cfg(f128_enabled)] | |
| impl_op_for_ty_no_f16!(f128, "f128"); | |
| #[cfg(f16_enabled)] | |
| impl_op_for_ty_all!(f16, "f16"); | |
| impl_op_for_ty_all!(f32, "f"); | |
| impl_op_for_ty_all!(f64, ""); | |
| #[cfg(f128_enabled)] | |
| impl_op_for_ty_all!(f128, "f128"); | |
| #[cfg(f16_enabled)] | |
| impl_extend_trunc!(f16, f32); | |
| #[cfg(f16_enabled)] | |
| impl_extend_trunc!(f16, f64); | |
| #[cfg(f16_enabled)] | |
| #[cfg(f128_enabled)] | |
| impl_extend_trunc!(f16, f128); | |
| impl_extend_trunc!(f32, f64); | |
| #[cfg(f128_enabled)] | |
| impl_extend_trunc!(f32, f128); | |
| #[cfg(f128_enabled)] | |
| impl_extend_trunc!(f64, f128); | |
| impl_ftoi_itof!(f32, i32); | |
| impl_ftoi_itof!(f32, i64); | |
| impl_ftoi_itof!(f32, i128); | |
| impl_ftoi_itof!(f32, u32); | |
| impl_ftoi_itof!(f32, u64); | |
| impl_ftoi_itof!(f32, u128); | |
| impl_ftoi_itof!(f64, i32); | |
| impl_ftoi_itof!(f64, i64); | |
| impl_ftoi_itof!(f64, i128); | |
| impl_ftoi_itof!(f64, u32); | |
| impl_ftoi_itof!(f64, u64); | |
| impl_ftoi_itof!(f64, u128); | |
| #[cfg(f128_enabled)] | |
| impl_ftoi_itof!(f128, i32); | |
| #[cfg(f128_enabled)] | |
| impl_ftoi_itof!(f128, i64); | |
| #[cfg(f128_enabled)] | |
| impl_ftoi_itof!(f128, i128); | |
| #[cfg(f128_enabled)] | |
| impl_ftoi_itof!(f128, u32); | |
| #[cfg(f128_enabled)] | |
| impl_ftoi_itof!(f128, u64); | |
| #[cfg(f128_enabled)] | |
| impl_ftoi_itof!(f128, u128); | |
| // `lgamma_r` is not a simple suffix so we can't use the above macro. | |
| impl MpOp for crate::op::lgamma_r::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| let (sign, ord) = this.ln_abs_gamma_round(Nearest); | |
| let ret = prep_retval::<Arg0<Self>>(this, ord); | |
| (ret, sign as i32) | |
| } | |
| } | |
| impl MpOp for crate::op::lgammaf_r::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| let (sign, ord) = this.ln_abs_gamma_round(Nearest); | |
| let ret = prep_retval::<Arg0<Self>>(this, ord); | |
| (ret, sign as i32) | |
| } | |
| } | |
| impl MpOp for crate::op::lgamma::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| <crate::op::lgamma_r::Routine as MpOp>::run(this, input).0 | |
| } | |
| } | |
| impl MpOp for crate::op::lgammaf::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| new_mpfloat::<Arg0<Self>>() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| <crate::op::lgammaf_r::Routine as MpOp>::run(this, input).0 | |
| } | |
| } | |
| /* stub implementations so we don't need to special case them */ | |
| impl MpOp for crate::op::nextafter::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| unimplemented!("nextafter does not yet have a MPFR operation"); | |
| } | |
| fn run(_this: &mut Self::MpTy, _input: Self::RustArgs) -> Self::RustRet { | |
| unimplemented!("nextafter does not yet have a MPFR operation"); | |
| } | |
| } | |
| impl MpOp for crate::op::nextafterf::Routine { | |
| type MpTy = MpFloat; | |
| fn new_mp() -> Self::MpTy { | |
| unimplemented!("nextafter does not yet have a MPFR operation"); | |
| } | |
| fn run(_this: &mut Self::MpTy, _input: Self::RustArgs) -> Self::RustRet { | |
| unimplemented!("nextafter does not yet have a MPFR operation"); | |
| } | |
| } | |
| macro_rules! impl_int_ops { | |
| ($ity:ty) => { | |
| paste::paste! { | |
| impl MpOp for crate::op::[<idiv_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| if input.1 == 0 { | |
| // Make divide by 0 well-defined to match our wrappers. | |
| return <$ity>::MIN; | |
| } | |
| this.assign(input.0); | |
| *this /= input.1; | |
| (&*this).wrapping_as::<Self::RustRet>() | |
| } | |
| } | |
| impl MpOp for crate::op::[<imod_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| if input.1 == 0 { | |
| // Make divide by 0 well-defined to match our wrappers. | |
| return <$ity>::MIN; | |
| } | |
| this.assign(input.0); | |
| *this %= input.1; | |
| (&*this).wrapping_as::<Self::RustRet>() | |
| } | |
| } | |
| impl MpOp for crate::op::[<idivmod_ $ity>]::Routine { | |
| type MpTy = (MpInt, MpInt); | |
| fn new_mp() -> Self::MpTy { | |
| (MpInt::new(), MpInt::new()) | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| if input.1 == 0 { | |
| // Make divide by 0 well-defined to match our wrappers. | |
| return (<$ity>::MIN, <$ity>::MIN); | |
| } | |
| this.0.assign(input.0); | |
| this.1.assign(input.1); | |
| this.0.div_rem_mut(&mut this.1); | |
| ( | |
| (&this.0).wrapping_as::<Ret0<Self>>(), | |
| (&this.1).wrapping_as::<Ret1<Self>>(), | |
| ) | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| impl_int_ops!(i32); | |
| impl_int_ops!(i64); | |
| impl_int_ops!(i128); | |
| impl_int_ops!(u32); | |
| impl_int_ops!(u64); | |
| impl_int_ops!(u128); | |
| macro_rules! impl_unsigned_int_ops { | |
| ($ity:ty) => { | |
| paste::paste! { | |
| impl MpOp for crate::op::[<ashl_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| assert!(input.1 < Arg0::<Self>::BITS, "got UB shift {}", input.1); | |
| this.assign(input.0); | |
| *this <<= input.1; | |
| (&*this).wrapping_as::<Arg0<Self>>() | |
| } | |
| } | |
| impl MpOp for crate::op::[<lshr_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| assert!(input.1 < Arg0::<Self>::BITS, "got UB shift {}", input.1); | |
| this.assign(input.0); | |
| *this >>= input.1; | |
| (&*this).wrapping_as::<Arg0<Self>>() | |
| } | |
| } | |
| impl MpOp for crate::op::[<leading_zeros_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| (Arg0::<Self>::BITS - this.significant_bits()).try_into().unwrap() | |
| } | |
| } | |
| impl MpOp for crate::op::[<trailing_zeros_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| this.find_one(0).unwrap_or(Arg0::<Self>::BITS).try_into().unwrap() | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| impl_unsigned_int_ops!(u32); | |
| impl_unsigned_int_ops!(u64); | |
| impl_unsigned_int_ops!(u128); | |
| macro_rules! impl_signed_int_ops { | |
| ($ity:ty) => { | |
| paste::paste! { | |
| impl MpOp for crate::op::[<ashr_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| assert!(input.1 < Arg0::<Self>::BITS, "got UB shift {}", input.1); | |
| this.assign(input.0); | |
| *this >>= input.1; | |
| (&*this).wrapping_as::<Self::RustRet>() | |
| } | |
| } | |
| impl MpOp for crate::op::[<imulo_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| *this *= input.1; | |
| (&*this).overflowing_cast() | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| impl_signed_int_ops!(i32); | |
| impl_signed_int_ops!(i64); | |
| impl_signed_int_ops!(i128); | |
| macro_rules! impl_u128_i128_int_ops { | |
| ($ity:ty) => { | |
| paste::paste! { | |
| impl MpOp for crate::op::[<iadd_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| *this += input.1; | |
| (&*this).wrapping_as::<Self::RustRet>() | |
| } | |
| } | |
| impl MpOp for crate::op::[<isub_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| *this -= input.1; | |
| (&*this).wrapping_as::<Self::RustRet>() | |
| } | |
| } | |
| impl MpOp for crate::op::[<iaddo_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| *this += input.1; | |
| (&*this).overflowing_cast() | |
| } | |
| } | |
| impl MpOp for crate::op::[<isubo_ $ity>]::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| *this -= input.1; | |
| (&*this).overflowing_cast() | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| impl_u128_i128_int_ops!(i128); | |
| impl_u128_i128_int_ops!(u128); | |
| impl MpOp for crate::op::imul_u64::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| *this *= input.1; | |
| (&*this).wrapping_as() | |
| } | |
| } | |
| impl MpOp for crate::op::imul_i128::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| *this *= input.1; | |
| (&*this).wrapping_as() | |
| } | |
| } | |
| impl MpOp for crate::op::imulo_u128::Routine { | |
| type MpTy = MpInt; | |
| fn new_mp() -> Self::MpTy { | |
| MpInt::new() | |
| } | |
| fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { | |
| this.assign(input.0); | |
| *this *= input.1; | |
| (&*this).overflowing_cast() | |
| } | |
| } |