Heron's Formula provides a remarkable method for calculating the area of a triangle when the lengths of all three sides are known, without needing to measure angles or heights. By determining the semi-perimeter of the triangle as half the sum of its side lengths, Heron's Formula then uses this value in a square root expression that multiplies the semi-perimeter by the difference between the semi-perimeter and each of the side lengths.
The following program compares using 32 bit and 64 bit floating point numbers in Rust with in Heron's formula. I also added the Cayley-Menger determinant formula for comparison, but this gave less accurate results.
The Rust code
fn main() { // set values for a, b and c let a = 12345679.0; let b = 12345678.0; let cs = [4.0, 2.0, 1.1, 1.01]; // print header println!("{:<10} | {:<20} | {:<20} | {:<20} | {:<20} | {:<20}", "c", "Area 32 bit eqn 1", "Area 64 bit eqn 1", "Area 32 bit eqn 2", "Area 64 bit eqn 2", "Area 64 bit cayley"); // loop through values of c and print results for &c in &cs { let area_32_eq1 = herons_formula_32(a as f32, b as f32, c as f32); let area_64_eq1 = herons_formula_64(a, b, c); let area_32_eq2 = herons_formula_alt_32(a as f32, b as f32, c as f32); let area_64_eq2 = herons_formula_alt_64(a, b, c); let area_64_cayley = cayley_menger_area(a, b, c); println!("{:<10} | {:<20} | {:<20} | {:<20} | {:<20} | {:<20 }", c, area_32_eq1, area_64_eq1, area_32_eq2, area_64_eq2, area_64_cayley); } } // Heron's formula using 32-bit precision fn herons_formula_32(a: f32, b: f32, c: f32) -> f32 { let s = (a + b + c) / 2.0; (s * (s - a) * (s - b) * (s - c)).sqrt() } // Heron's formula using 64-bit precision fn herons_formula_64(a: f64, b: f64, c: f64) -> f64 { let s = (a + b + c) / 2.0; (s * (s - a) * (s - b) * (s - c)).sqrt() } // Alternative Heron's formula using 32-bit precision fn herons_formula_alt_32(a: f32, b: f32, c: f32) -> f32 { ((a + (b + c)) * (c - (a - b)) * (c + (a - b)) * (a + (b - c))).sqrt() / 4.0 } // Alternative Heron's formula using 64-bit precision fn herons_formula_alt_64(a: f64, b: f64, c: f64) -> f64 { ((a + (b + c)) * (c - (a - b)) * (c + (a - b)) * (a + (b - c))).sqrt() / 4.0 } // Calculate area using Cayley Menger Determinant fn cayley_menger_area(a: f64, b: f64, c: f64) -> f64 { // The determinant is multiplied by -1 to get 16A^2, then take the square root and divide by 4 let determinant = 2.0*a.powi(2)*b.powi(2) + 2.0*b.powi(2)*c.powi(2) + 2.0*c.powi(2)*a.powi(2) - a.powi(4) - b.powi(4) - c.powi(4); //let area_squared = -determinant / 16.0; // This gives us A^2 //area_squared.sqrt() // Return the square root of A^2 to get A (determinant / 16.0).sqrt() }
The code output
a = 12345679, b = 12345678 c | 32 bit eqn 1 | Area 64 bit eqn 1 | 32 bit eqn 2 | 64 bit eqn 2 | 64 bit cayley -----|--------------|--------------------|-----------------|---------------------|--------------------- 4 | 17459426 | 23907303.61406517 | 23907304 | 23907303.61406517 | 23899713.1913673 2 | 0 | 10691671.207955327 | 10691672 | 10691671.207955327 | 10719093.483247126 1.1 | 0 | 2828750.333671384 | 2828750.8 | 2828750.3115919423 | 2871643.6422926695 1.01 | 0 | 875151.0776609103 | 875150.56 | 875151.0055798625 | 741455.2001894214
Mission Accomplished
As you can see, the area calculation using 32 bit with eqn 1 gives incorrect results, but the other outputs are, with in variaing accuracy.
For c=4 The 32 bit calculation gives a slightly off result for eqn 1 due to precision loss in intermediate steps. This is because the large numbers involved in the calculation exceed the precision that f32 can accurately represent.
For c=2, c=1.1, and c=1.01 the results are 0 for the 32 bit eqn 1 calculations because the precision loss is so significant that the computed area underflows to zero. The subtraction operations in Heron's formula (particularly s - a and s - b) result in values close to zero, which, when multiplied together, result in underflow for 32 bit.