///|
fn render_number(value : Value) -> String raise ParseError {
match number_value(value) {
Integer(n) => n.to_string()
Long(n) => n.to_string()
Floating(n) => render_double(n)
}
}
///|
// Reuse the runtime's nearest shortest decimal and normalize the JDK layout.
// A tiny one-digit subnormal needs rounding to a second digit instead:
// e.g. the smallest subnormal is 4.9E-324, not the runtime's 5e-324.
fn render_double(value : Double) -> String raise ParseError {
if value.is_nan() {
return "NaN"
}
if value.is_inf() {
return if value > 0.0 { "Infinity" } else { "-Infinity" }
}
let bits = value.reinterpret_as_int64()
let sign = if bits < 0L { "-" } else { "" }
if value == 0.0 {
return sign + "0.0"
}
let raw = (if value < 0.0 { -value } else { value }).to_string()
let digits = StringBuilder()
let mut point = -1
let mut shift = 0
let mut count = 0
for i = 0; i < raw.length(); i = i + 1 {
match raw[i] {
46 => point = count
69 | 101 => {
shift = @string.parse_int(raw.view(start_offset=i + 1)) catch {
_ => raise Invalid("decimal exponent conversion")
}
break
}
digit => {
digits.write_char(digit.to_int().unsafe_to_char())
count += 1
}
}
}
if point < 0 {
point = count
}
let digits = digits.to_string()
let mut start = 0
let mut end = digits.length()
while start < end - 1 && digits[start] == 48 {
start += 1
}
let exponent = shift + point - start - 1
while end > start + 1 && digits[end - 1] == 48 {
end -= 1
}
// Above this threshold, half an ulp is smaller than half the spacing of
// two-digit decimals, including the finer spacing below a decimal power.
if end - start > 1 || exponent >= -321 {
return format_double_digits(
sign,
digits.view(start_offset=start, end_offset=end).to_owned(),
exponent,
)
}
render_tiny_double(bits, sign)
}
///|
fn render_tiny_double(bits : Int64, sign : String) -> String raise ParseError {
let mantissa = bits & 4503599627370495L
// MIN_VALUE lies strictly between A * 10^-339 and (A + 1) * 10^-339.
// Propagate this exact interval through integer arithmetic. A boundary
// overlap falls back to full precision; no floating-point rounding is used.
// The caller's one-digit exponent <= -322 implies mantissa <= 182.
// Retain a guard for the multiplication if this helper's domain changes.
if mantissa > 1000L {
return render_double_exact(bits, sign)
}
let lower = mantissa * 4940656458412465L
let upper = lower + mantissa
let length = lower.to_string().length()
if upper.to_string().length() != length {
return render_double_exact(bits, sign)
}
let mut divisor = 1L
for _ in 0..<(length - 2) {
divisor *= 10L
}
let remainder = lower % divisor
if remainder * 2L <= divisor && (remainder + mantissa) * 2L >= divisor {
return render_double_exact(bits, sign)
}
let significant = lower / divisor +
(if remainder * 2L > divisor { 1L } else { 0L })
let digits = significant.to_string()
let exponent = length - 340 + digits.length() - 2
let mut end = digits.length()
while end > 1 && digits[end - 1] == 48 {
end -= 1
}
format_double_digits(sign, digits.view(end_offset=end).to_owned(), exponent)
}
///|
fn render_double_exact(bits : Int64, sign : String) -> String raise ParseError {
let exponent = ((bits >> 52) & 2047L).to_int()
let fraction = bits & 4503599627370495L
let mantissa = if exponent == 0 {
fraction
} else {
fraction + 4503599627370496L
}
let power = if exponent == 0 { -1074 } else { exponent - 1075 }
let mut integer = @bigint.BigInt::from_int64(mantissa)
let scale = if power >= 0 { 0 } else { -power }
if power >= 0 {
integer = integer << power
} else {
integer = integer *
@bigint.BigInt::from_int(5).pow(@bigint.BigInt::from_int(scale))
}
let exact = integer.to_string()
let original_exponent = exact.length() - scale - 1
let scientific = original_exponent < -3 || original_exponent >= 7
for precision = (if scientific { 2 } else { 1 })
precision <= 17
precision = precision + 1 {
let count = precision.min(exact.length())
let prefix = exact.view(end_offset=count).to_owned()
let mut significant = @string.parse_int64(prefix) catch {
_ => raise Invalid("decimal conversion")
}
if count < exact.length() {
let next = exact[count].to_int()
let mut tail = false
for i = count + 1; i < exact.length(); i = i + 1 {
if exact[i] != 48 {
tail = true
break
}
}
if next > 53 || (next == 53 && (tail || significant % 2L == 1L)) {
significant += 1L
}
}
let digits = significant.to_string()
let exponent = original_exponent + digits.length() - count
let candidate = digits.view(end_offset=1).to_owned() +
"." +
(if digits.length() == 1 {
"0"
} else {
digits.view(start_offset=1).to_owned()
}) +
"e" +
exponent.to_string()
let rounded = double_value(sign + candidate)
if rounded.reinterpret_as_int64() == bits {
let mut end = digits.length()
while end > 1 && digits[end - 1] == 48 {
end -= 1
}
let digits = digits.view(end_offset=end).to_owned()
return format_double_digits(sign, digits, exponent)
}
}
raise Invalid("decimal conversion failed")
}
///|
fn format_double_digits(
sign : String,
digits : String,
exponent : Int,
) -> String {
if exponent < -3 || exponent >= 7 {
return sign +
digits.view(end_offset=1).to_owned() +
"." +
(if digits.length() > 1 {
digits.view(start_offset=1).to_owned()
} else {
"0"
}) +
"E" +
exponent.to_string()
}
let output = StringBuilder()
output.write_string(sign)
let point = exponent + 1
if point <= 0 {
output.write_string("0.")
for _ in 0..<-point {
output.write_string("0")
}
output.write_string(digits)
} else if point >= digits.length() {
output.write_string(digits)
for _ in digits.length()..