///|
fn compat_signed_man(x : Mpf) -> BigInt {
if x.sign == 1 {
-x.man
} else {
x.man
}
}
///|
/// Normalize a raw mantissa/exponent tuple with explicit precision and rounding.
pub fn mpf_normalize(
sign : Int,
man : BigInt,
exp : Int,
bc : Int,
prec : Int,
rnd : RoundMode,
) -> Mpf {
@mpf.normalize(sign, man, exp, bc, prec, rnd)
}
///|
/// Construct an `mpf` from a signed mantissa and power-of-two exponent.
pub fn from_man_exp(
man : BigInt,
exp : Int,
prec? : Int = 0,
rnd? : RoundMode = round_down,
) -> Mpf {
@mpf.from_man_exp(man, exp, prec, rnd)
}
///|
/// Construct an exact `mpf` from a machine integer.
pub fn from_int(n : Int) -> Mpf {
@mpf.from_int(n)
}
///|
/// Parse a decimal string into an `mpf`.
pub fn from_str(
s : String,
prec? : Int = 53,
rnd? : RoundMode = round_nearest,
) -> Mpf raise MpfError {
@mpf.from_str(s, prec~, rnd~)
}
///|
/// Construct an `mpf` from an exact rational `p/q`.
pub fn from_rational(
p : BigInt,
q : BigInt,
prec : Int,
rnd? : RoundMode = round_nearest,
) -> Mpf raise MpfError {
if q == 0N {
raise @mpf.MpfError::DivisionByZero("from_rational: denominator is zero")
}
@mpf.mpf_div(from_man_exp(p, 0), from_man_exp(q, 0), prec, rnd)
}
///|
/// Exact conversion from a machine `Double` into an `mpf`.
pub fn from_float(
x : Double,
prec? : Int = 53,
rnd? : RoundMode = round_nearest,
) -> Mpf {
let bits = Double::reinterpret_as_uint64(x)
let zero_u = UInt64::default()
let one_u = UInt64::extend_uint(1)
let sign = (bits >> 63) & one_u
let exp_mask = (one_u << 11) - one_u
let frac_mask = (one_u << 52) - one_u
let exp_u = (bits >> 52) & exp_mask
let frac_u = bits & frac_mask
let exp_bits = exp_u.to_int()
if exp_bits == 0x7ff {
if frac_u == zero_u {
if sign == zero_u {
finf
} else {
fninf
}
} else {
fnan
}
} else if exp_bits == 0 {
if frac_u == zero_u {
fzero
} else {
let man0 = BigInt::from_uint64(frac_u)
let man = if sign == zero_u { man0 } else { -man0 }
@mpf.from_man_exp(man, -1074, prec, rnd)
}
} else {
let man0 = BigInt::from_uint64(frac_u) + (1N << 52)
let man = if sign == zero_u { man0 } else { -man0 }
@mpf.from_man_exp(man, exp_bits - 1075, prec, rnd)
}
}
///|
/// Convert an `mpf` into an exact rational pair `(p, q)`.
pub fn to_rational(x : Mpf) -> (BigInt, BigInt) raise MpfError {
if @mpf.is_nan(x) || @mpf.is_inf(x) {
raise @mpf.MpfError::ValueError("to_rational: cannot convert nan/inf")
}
let man = compat_signed_man(x)
if x.exp >= 0 {
(man << x.exp, 1N)
} else {
(man, 1N << -x.exp)
}
}
///|
/// Convert an `mpf` to an arbitrary-precision integer.
pub fn to_int(x : Mpf, rnd? : RoundMode = round_down) -> BigInt raise MpfError {
if @mpf.is_nan(x) || @mpf.is_inf(x) {
raise @mpf.MpfError::ValueError("to_int: cannot convert nan/inf")
}
if x.exp >= 0 {
compat_signed_man(x) << x.exp
} else {
let shift = -x.exp
let mag = x.man
match rnd {
RoundMode::Down =>
if x.sign == 1 {
-(mag >> shift)
} else {
mag >> shift
}
RoundMode::Floor =>
if x.sign == 1 {
-((mag + ((1N << shift) - 1N)) >> shift)
} else {
mag >> shift
}
RoundMode::Ceiling =>
if x.sign == 1 {
-(mag >> shift)
} else {
(mag + ((1N << shift) - 1N)) >> shift
}
RoundMode::Up =>
if (mag & ((1N << shift) - 1N)) == 0N {
if x.sign == 1 {
-(mag >> shift)
} else {
mag >> shift
}
} else if x.sign == 1 {
-((mag + ((1N << shift) - 1N)) >> shift)
} else {
(mag + ((1N << shift) - 1N)) >> shift
}
RoundMode::Nearest => {
let int_part = mag >> shift
let rem_mask = (1N << shift) - 1N
let rem = mag & rem_mask
let half = 1N << (shift - 1)
let rounded = if rem > half || (rem == half && (int_part & 1N) == 1N) {
int_part + 1N
} else {
int_part
}
if x.sign == 1 {
-rounded
} else {
rounded
}
}
}
}
}
///|
/// Convert an `mpf` to the nearest machine `Double`.
pub fn to_float(
x : Mpf,
strict? : Bool = false,
rnd? : RoundMode = round_nearest,
) -> Double raise MpfError {
if @mpf.is_zero(x) {
return 0.0
}
if x == finf {
return 1.0 / 0.0
}
if x == fninf {
return -1.0 / 0.0
}
if @mpf.is_nan(x) {
return 0.0 / 0.0
}
let y = if x.bc > 53 {
@mpf.normalize(x.sign, x.man, x.exp, x.bc, 53, rnd)
} else {
x
}
let value = @string.parse_double(@mpf.to_str_opts(y, dps=17)) catch {
_ =>
raise @mpf.MpfError::ValueError(
"to_float: failed to parse decimal rendering",
)
}
if strict && value == 0.0 {
raise @mpf.MpfError::ValueError("to_float: underflow")
}
if strict && (value == 1.0 / 0.0 || value == -1.0 / 0.0) {
raise @mpf.MpfError::ValueError("to_float: overflow")
}
value
}
///|
/// Convert an `mpf` to a printable decimal or radix string.
pub fn to_str(
x : Mpf,
dps? : Int = 15,
base? : Int = 10,
binary_exp? : Bool = false,
) -> String raise MpfError {
@mpf.to_str_opts(x, dps~, base~, binary_exp~)
}
///|
/// Format an `mpf` using the libmp formatting mini-language.
pub fn format_mpf(
x : Mpf,
format_spec : String,
dps? : Int = 15,
) -> String raise MpfError {
@mpf.format_mpf(x, format_spec, dps~)
}
///|
/// Convenience alias mirroring mpmath's `make_mpf` naming.
pub fn make_mpf(
s : String,
prec? : Int = 53,
rnd? : RoundMode = round_nearest,
) -> Mpf raise MpfError {
from_str(s, prec~, rnd~)
}
///|
/// Build a complex value from real and imaginary parts.
pub fn make_mpc(real : Mpf, imag? : Mpf = fzero) -> Mpc {
@mpc.from_parts(real, imag)
}