///|
fn format_engineering(
value : Double,
unit : String,
places : Int32,
sep : String,
) -> String {
if numeric_special(value) is Some(text) {
return if unit == "" { text } else { text + sep + unit }
}
let magnitude = Double::abs(value)
let mut exponent = if magnitude == 0.0 {
0
} else {
// MoonBit integer division truncates toward zero. Engineering notation
// needs mathematical floor so values between 0 and 1 use milli/micro
// prefixes instead of the empty prefix.
let decade = decimal_exponent(magnitude)
(decade.to_double() / 3.0).floor().to_int() * 3
}
if exponent < -30 {
exponent = -30
}
if exponent > 30 {
exponent = 30
}
let mut mantissa = if magnitude == 0.0 {
0.0
} else {
let scaled = magnitude / @math.pow(10.0, Double::from_int(exponent))
if value < 0.0 {
-scaled
} else {
scaled
}
}
let precision = if places < 0 { None } else { Some(places) }
let mut rendered = match precision {
Some(digits) => format_fixed(mantissa, digits)
None => format_general(mantissa, None, false)
}
// Promote only when the selected precision actually rounds the mantissa to
// 1000. A fixed two-place formatter must keep 999.90 k, while a default
// six-significant-digit formatter promotes 999.9999 to 1 k.
let rounded_magnitude = Double::abs(try! @string.parse_double(rendered[:]))
if rounded_magnitude >= 1000.0 && exponent < 30 {
exponent += 3
mantissa /= 1000.0
rendered = match precision {
Some(digits) => format_fixed(mantissa, digits)
None => format_general(mantissa, None, false)
}
}
let prefix = engineering_prefix(exponent)
if prefix == "" && unit == "" {
rendered
} else {
rendered + sep + prefix + unit
}
}
///|
fn engineering_prefix(exponent : Int32) -> String {
match exponent {
-30 => "q"
-27 => "r"
-24 => "y"
-21 => "z"
-18 => "a"
-15 => "f"
-12 => "p"
-9 => "n"
-6 => "ยต"
-3 => "m"
0 => ""
3 => "k"
6 => "M"
9 => "G"
12 => "T"
15 => "P"
18 => "E"
21 => "Z"
24 => "Y"
27 => "R"
30 => "Q"
_ => ""
}
}