use core::ops::RangeInclusive;
pub const INT_RANGE: RangeInclusive<i64> = -2_147_483_648..=4_294_967_295;
#[must_use]
pub fn narrow_to_signed32(v: i64) -> i64 {
debug_assert!(
INT_RANGE.contains(&v),
"Object::Int outside the reachable parse range"
);
#[expect(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "reproducing the C++ narrowing exactly is the point"
)]
let bits = v as u32;
i64::from(bits.cast_signed())
}
#[must_use]
#[expect(
clippy::cast_precision_loss,
reason = "matching C++ uint32->float / int32->float, which rounds the same way"
)]
pub fn widen_to_f32(v: i64) -> f32 {
debug_assert!(
INT_RANGE.contains(&v),
"Object::Int outside the reachable parse range"
);
v as f32
}
#[must_use]
pub fn truncate_to_signed32(v: f32) -> i64 {
if v.is_nan() {
return 0;
}
#[expect(
clippy::cast_possible_truncation,
reason = "saturating float->int is exactly the C++ behavior being matched"
)]
i64::from(v as i32)
}
fn shortest_decimal(v: f32) -> (Vec<u8>, i32) {
let mut buf = ryu::Buffer::new();
let s = buf.format_finite(v);
let s = s.strip_prefix('-').unwrap_or(s);
let (mantissa, exp10) = match s.split_once(['e', 'E']) {
Some((m, e)) => (m, e.parse::<i32>().unwrap_or(0)),
None => (s, 0),
};
let (int_part, frac_part) = mantissa.split_once('.').unwrap_or((mantissa, ""));
let mut digits: Vec<u8> = int_part
.bytes()
.chain(frac_part.bytes())
.skip_while(|b| *b == b'0')
.collect();
let frac_len = i32::try_from(frac_part.len()).unwrap_or(i32::MAX);
let mut exponent = exp10.saturating_sub(frac_len);
while digits.len() > 1 && digits.last() == Some(&b'0') {
digits.pop();
exponent = exponent.saturating_add(1);
}
(digits, exponent)
}
const MAX_FLOAT_DECIMAL_LEN: usize = 48;
#[must_use]
pub fn fmt_number(value: f32) -> String {
if value.is_nan() || value == 0.0 {
return "0".to_owned();
}
let mut magnitude = if value == f32::INFINITY {
f32::MAX
} else if value == f32::NEG_INFINITY {
f32::MIN
} else {
value
};
let mut out = String::new();
if magnitude < 0.0 {
out.push('-');
magnitude = -magnitude;
}
let (digits, exponent) = shortest_decimal(magnitude);
let digit_count = i32::try_from(digits.len()).unwrap_or(i32::MAX);
if exponent >= 0 {
out.push_str(&String::from_utf8_lossy(&digits));
for _ in 0..exponent {
out.push('0');
}
return out;
}
let places_before_point = digit_count + exponent;
if places_before_point > 0 {
let split = usize::try_from(places_before_point).unwrap_or(0);
let (whole, fraction) = digits.split_at(split.min(digits.len()));
out.push_str(&String::from_utf8_lossy(whole));
out.push('.');
out.push_str(&String::from_utf8_lossy(fraction));
return out;
}
out.push('.');
for _ in 0..-places_before_point {
out.push('0');
}
for d in digits {
out.push(char::from(d));
if out.len() >= MAX_FLOAT_DECIMAL_LEN {
break;
}
}
out
}
#[must_use]
pub fn fmt_int(value: i64) -> String {
narrow_to_signed32(value).to_string()
}
#[cfg(test)]
#[expect(
clippy::float_cmp,
reason = "these assertions pin exact bit patterns the oracle produces"
)]
mod tests {
use super::{fmt_int, fmt_number, narrow_to_signed32, truncate_to_signed32, widen_to_f32};
#[test]
fn integer_view_wraps_the_unsigned_range() {
assert_eq!(narrow_to_signed32(0), 0);
assert_eq!(narrow_to_signed32(1245), 1245);
assert_eq!(narrow_to_signed32(-2_147_483_648), -2_147_483_648);
assert_eq!(narrow_to_signed32(2_147_483_647), 2_147_483_647);
assert_eq!(narrow_to_signed32(2_147_483_648), -2_147_483_648);
assert_eq!(narrow_to_signed32(4_294_967_295), -1);
assert_eq!(narrow_to_signed32(4_294_967_294), -2);
}
#[test]
fn numeric_view_widens_instead_of_wrapping() {
assert_eq!(widen_to_f32(0), 0.0);
assert_eq!(widen_to_f32(1245), 1245.0);
assert_eq!(widen_to_f32(-2_147_483_648), -2_147_483_648.0);
assert_eq!(widen_to_f32(4_294_967_295), 4_294_967_296.0);
}
#[test]
fn real_to_integer_saturates_and_zeroes_nan() {
assert_eq!(truncate_to_signed32(5.2), 5);
assert_eq!(truncate_to_signed32(9.003_45), 9);
assert_eq!(truncate_to_signed32(-0.5), 0);
assert_eq!(truncate_to_signed32(f32::NAN), 0);
assert_eq!(truncate_to_signed32(f32::INFINITY), i64::from(i32::MAX));
assert_eq!(truncate_to_signed32(f32::NEG_INFINITY), i64::from(i32::MIN));
assert_eq!(truncate_to_signed32(1e30), i64::from(i32::MAX));
}
#[test]
fn float_spelling_matches_the_oracle() {
let cases: &[(f32, &str)] = &[
(0.0, "0"),
(-0.0, "0"),
(1.0, "1"),
(-1.0, "-1"),
(0.5, ".5"),
(-0.5, "-.5"),
(0.001_25, ".00125"),
(123.45, "123.45"),
(-7.5, "-7.5"),
(38.895_285, "38.895287"),
(-77.037_23, "-77.03723"),
(9.003_45, "9.00345"),
(0.23, ".23"),
(f32::MAX, "340282350000000000000000000000000000000"),
(-f32::MAX, "-340282350000000000000000000000000000000"),
(
f32::MIN_POSITIVE,
".000000000000000000000000000000000000011754944",
),
(
-f32::MIN_POSITIVE,
"-.000000000000000000000000000000000000011754944",
),
(f32::INFINITY, "340282350000000000000000000000000000000"),
(
f32::NEG_INFINITY,
"-340282350000000000000000000000000000000",
),
(f32::NAN, "0"),
];
for (value, want) in cases {
assert_eq!(&fmt_number(*value), want, "spelling {value}");
}
}
#[test]
fn smallest_denormal_hits_the_writer_length_cap() {
let smallest = f32::from_bits(1);
assert_eq!(fmt_number(-smallest).len(), 47);
assert!(fmt_number(smallest).len() <= 48);
}
#[test]
fn integer_spelling_matches_the_oracle() {
assert_eq!(fmt_int(0), "0");
assert_eq!(fmt_int(1), "1");
assert_eq!(fmt_int(-99), "-99");
assert_eq!(fmt_int(1234), "1234");
assert_eq!(fmt_int(-54321), "-54321");
assert_eq!(fmt_int(2_147_483_647), "2147483647");
assert_eq!(fmt_int(-2_147_483_648), "-2147483648");
assert_eq!(fmt_int(4_294_967_295), "-1");
}
}