///|
fn math_builtin(
name : String,
values : Array[Value],
loc : Location,
) -> Value raise {
if name == "xor" || name == "xnor" {
let x = boolean(values[0], loc)
let y = boolean(values[1], loc)
return Bool(if name == "xor" { x != y } else { x == y })
}
let x = number_value(values[0], loc)
match name {
"isEven" => Bool(x.trunc() % 2 == 0)
"isOdd" => Bool(x.trunc() % 2 != 0)
"isInteger" => Bool(x - x.trunc() == 0)
"isDecimal" => Bool(x - x.trunc() != 0)
"round" => {
let whole = x.trunc()
let rounded = if (x - whole).abs() >= 0.5 {
if x < 0 {
whole - 1
} else {
whole + 1
}
} else {
whole
}
finite(rounded, loc)
}
"mantissa" => finite(binary_fraction(x).0, loc)
"exponent" => Num(binary_fraction(x).1.to_double())
"modulo" => {
let y = number_value(values[1], loc)
if y == 0 {
raise problem(Runtime, loc, "Division by zero.")
}
finite(x % y, loc)
}
_ => {
let result = match name {
"sqrt" => x.sqrt()
"sin" | "cos" | "tan" => reference_trig(name, x)
"asin" => reference_asin(x)
"acos" => 1.5707963267948966 - reference_asin(x)
"atan" => reference_atan(x)
"log" => reference_log(x)
"exp" => reference_exp(x)
"hypot" => @math.hypot(x, number_value(values[1], loc))
"atan2" => reference_atan2(x, number_value(values[1], loc))
"pow" => reference_pow(x, number_value(values[1], loc))
_ => raise problem(Unsupported, loc, "unsupported math function")
}
finite(result, loc)
}
}
}
///|
// IEEE binary64 decomposition, including normalization of subnormal values.
fn binary_fraction(x : Double) -> (Double, Int) {
if x == 0 || x.is_inf() || x.is_nan() {
return (x, 0)
}
let small = x.abs() < 2.2250738585072014e-308
let normalized = if small { x * 18014398509481984.0 } else { x }
let bits = normalized.reinterpret_as_int64()
let exponent = (bits >> 52).land(2047L).to_int() -
1022 -
(if small { 54 } else { 0 })
let fraction = bits
.land(0x7ff0000000000000L.lnot())
.lor(0x3fe0000000000000L)
.reinterpret_as_double()
(fraction, exponent)
}