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.