From 3b8c638a845ac21cb42a87434130dbd8e7b5107e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20W=C3=BClker?= Date: Wed, 21 Aug 2024 12:41:43 +0200 Subject: [PATCH] Fix floating point errors in table layout (#33098) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Simon Wülker --- components/layout_2020/table/layout.rs | 32 ++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs index 9b0a18d8cdd..e0552481b65 100644 --- a/components/layout_2020/table/layout.rs +++ b/components/layout_2020/table/layout.rs @@ -890,17 +890,45 @@ impl<'a> TableLayout<'a> { } let bounds = |sum_a, sum_b| self.assignable_width > sum_a && self.assignable_width < sum_b; + let blend = |a: &[Au], sum_a: Au, b: &[Au], sum_b: Au| { // First convert the Au units to f32 in order to do floating point division. let weight_a = (self.assignable_width - sum_b).to_f32_px() / (sum_a - sum_b).to_f32_px(); let weight_b = 1.0 - weight_a; - a.iter() + + let mut sum_accounting_for_floating_point_inaccuracy = Au::new(0); + let mut widths: Vec = a + .iter() .zip(b.iter()) .map(|(guess_a, guess_b)| { (guess_a.scale_by(weight_a)) + (guess_b.scale_by(weight_b)) }) - .collect() + .inspect(|&column_width| { + sum_accounting_for_floating_point_inaccuracy += column_width; + }) + .collect(); + + if sum_accounting_for_floating_point_inaccuracy != self.assignable_width { + // The computations above can introduce floating-point imprecisions. + // Since these errors are very small (+-1Au), it's fine to simply adjust + // the first column such that the total width matches the assignable width + let difference = + self.assignable_width - sum_accounting_for_floating_point_inaccuracy; + + debug_assert!( + difference.abs() <= Au::new(widths.len() as i32), + "A deviation of more than one Au per column is unlikely to be caused by float imprecision" + ); + + // We checked if the table was empty at the top of the function, so there + // always is a first column + widths[0] += difference; + } + + debug_assert!(widths.iter().sum::() == self.assignable_width); + + widths }; if bounds(min_content_sizes_sum, min_content_percentage_sizing_sum) {