Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ pub(crate) fn uuid_to_hex_string(uuid: Uuid) -> String {

pub fn truncate_float(float: f64, decimals: u32, round_up: bool) -> f64 {
let pow10 = 10i64.pow(decimals) as f64;
let mut float = (float * pow10) as u64;
let sign = float.signum();
let mut abs_truncated = (float.abs() * pow10) as u64;
if round_up {
float += 1;
abs_truncated += 1;
}
float as f64 / pow10
sign * abs_truncated as f64 / pow10
}

pub fn bps_diff(x: f64, y: f64) -> u16 {
Expand Down Expand Up @@ -135,4 +136,64 @@ mod tests {
"987654321".to_string()
);
}

#[test]
fn truncate_float_negative_values() {
// Regression: negatives previously cast to u64 and became 0.0
assert_eq!(truncate_float(-123.456, 2, false), -123.45);
assert_eq!(truncate_float(-99.999, 2, false), -99.99);
assert_eq!(truncate_float(-0.001, 4, false), -0.001);
assert_eq!(truncate_float(-0.00000001, 8, false), -0.00000001);
// decimals = 0
assert_eq!(truncate_float(-5.9, 0, false), -5.0);
}

#[test]
fn truncate_float_positive_unchanged() {
// Positive path must be byte-identical to the original implementation
assert_eq!(truncate_float(123.456, 2, false), 123.45);
assert_eq!(truncate_float(99.999, 2, false), 99.99);
assert_eq!(truncate_float(0.00000001, 8, false), 0.00000001);
assert_eq!(truncate_float(0.0, 2, false), 0.0);
}

#[test]
fn truncate_float_round_up_both_signs() {
// round_up grows magnitude (away from zero) for both signs
assert_eq!(truncate_float(123.456, 2, true), 123.46);
assert_eq!(truncate_float(-123.456, 2, true), -123.46);
assert_eq!(truncate_float(-5.9, 0, true), -6.0);
}

#[test]
fn truncate_float_zero_edges() {
// 0.0 and -0.0 both compare equal to 0.0
assert_eq!(truncate_float(0.0, 2, false), 0.0);
assert_eq!(truncate_float(-0.0, 2, false), 0.0);
}

#[test]
fn truncate_float_nan_propagates() {
// NaN input yields NaN (previous impl silently returned 0.0)
assert!(truncate_float(f64::NAN, 2, false).is_nan());
}

#[test]
fn truncate_float_antisymmetry_property() {
// Invariant: truncate(-x) == -truncate(x) for finite x, across
// many magnitudes, decimals, and the round_up flag.
for xi in 1..=2000u32 {
let x = f64::from(xi) * 0.0137;
for d in 0..=8u32 {
for round_up in [false, true] {
let neg = truncate_float(-x, d, round_up);
let pos = -truncate_float(x, d, round_up);
assert!(
(neg - pos).abs() < 1e-12,
"antisymmetry broke: x={x} d={d} round_up={round_up} neg={neg} pos={pos}"
);
}
}
}
}
}