Skip to content

Commit 3092287

Browse files
committed
fix: use relative tolerance for float comparison (handles scientific notation)
1 parent 40d93f1 commit 3092287

1 file changed

Lines changed: 13 additions & 32 deletions

File tree

src/optimization/history.rs

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,15 @@ pub use csv_io::*;
5656
// CSV load/save functionality - only available in tests (polars is a dev-dependency)
5757
#[cfg(test)]
5858
mod csv_io {
59-
/// Count decimal places in a numeric string (for precision-aware comparison)
60-
pub fn decimal_places(s: &str) -> usize {
61-
if let Some(dot_pos) = s.find('.') {
62-
s.len() - dot_pos - 1
59+
/// Compare f64 to expected string with relative tolerance
60+
pub fn float_eq(actual: f64, expected_str: &str) -> bool {
61+
let expected: f64 = expected_str.parse().expect("Failed to parse expected value");
62+
if expected == 0.0 {
63+
actual.abs() < 1e-14
6364
} else {
64-
0
65+
((actual - expected) / expected).abs() < 1e-12
6566
}
6667
}
67-
68-
/// Round f64 to specified decimal places, return as string
69-
pub fn round_to_string(val: f64, places: usize) -> String {
70-
format!("{:.prec$}", val, prec = places)
71-
}
72-
73-
/// Compare f64 to expected string, rounding actual to same precision as expected
74-
pub fn precision_eq(actual: f64, expected_str: &str) -> bool {
75-
let places = decimal_places(expected_str);
76-
let rounded_actual = round_to_string(actual, places);
77-
rounded_actual == expected_str
78-
}
7968
use std::collections::BTreeMap;
8069
use std::path::Path;
8170
use super::*;
@@ -231,18 +220,15 @@ mod csv_io {
231220
Ok(ExpectedHistory { col_names, steps })
232221
}
233222

234-
/// Compare actual HistoryStep against expected, using precision from expected strings
223+
/// Compare actual HistoryStep against expected with relative tolerance
235224
pub fn check_step(&self, step_idx: usize, actual: &HistoryStep) -> Result<(), String> {
236225
let expected = &self.steps[step_idx];
237226

238227
// Check error
239-
if !precision_eq(actual.error, &expected.error) {
228+
if !float_eq(actual.error, &expected.error) {
240229
return Err(format!(
241-
"Step {} error mismatch:\n expected: {}\n actual: {} (rounded: {})",
242-
step_idx,
243-
expected.error,
244-
actual.error,
245-
round_to_string(actual.error, decimal_places(&expected.error))
230+
"Step {} error mismatch:\n expected: {}\n actual: {}",
231+
step_idx, expected.error, actual.error
246232
));
247233
}
248234

@@ -256,16 +242,11 @@ mod csv_io {
256242
}
257243

258244
for (i, (actual_val, expected_str)) in actual_vals.iter().zip(expected.shape_vals.iter()).enumerate() {
259-
if !precision_eq(*actual_val, expected_str) {
245+
if !float_eq(*actual_val, expected_str) {
260246
let col_name = self.col_names.get(i + 1).map(|s| s.as_str()).unwrap_or("?");
261247
return Err(format!(
262-
"Step {} column {} ({}) mismatch:\n expected: {}\n actual: {} (rounded: {})",
263-
step_idx,
264-
i,
265-
col_name,
266-
expected_str,
267-
actual_val,
268-
round_to_string(*actual_val, decimal_places(expected_str))
248+
"Step {} column {} ({}) mismatch:\n expected: {}\n actual: {}",
249+
step_idx, i, col_name, expected_str, actual_val
269250
));
270251
}
271252
}

0 commit comments

Comments
 (0)