Heron's formula

Using Heron's formula in Rust

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.