1
\$\begingroup\$

I made a function in Rust to add two floating point numbers (f32s) together using only their bit representation and integer operations. I have tested it for a quite a few different cases but I'm not sure how I can efficiently test it for all floating point numbers without it taking ages.

fn add_f32(mut a: f32, mut b: f32) -> f32 {
    if b > a {
        // If b has a larger exponent than a, swap a and b so that a has the larger exponent
        core::mem::swap(&mut a, &mut b);
    }

    let a_normal = a.is_normal();
    let b_normal = b.is_normal();

    let a_bits = a.to_bits();
    let b_bits = b.to_bits();
    let mut a_exp = (a_bits << 1) >> (23 + 1);
    let mut b_exp = (b_bits << 1) >> (23 + 1);
    
    let mut a_mant = a_bits & 0x007fffff;
    let mut b_mant = b_bits & 0x007fffff;

    if a_exp == 0 {
        a_exp += 1;
    }
    if b_exp == 0 {
        b_exp += 1;
    }

    let exp_diff = a_exp - b_exp;

    let sticky_bit = b_mant.trailing_zeros() + 1 < exp_diff;

    // Add the implicit leading 1 bit to the mantissas
    if a_normal {
        a_mant |= 1 << 23;
    }
    if b_normal {
        b_mant |= 1 << 23;
    }

    // Append extra bits to the mantissas to ensure correct rounding
    a_mant <<= 2;
    b_mant <<= 2;

    // If the shift causes an overflow, the b_mant is too small so is set to 0
    b_mant = b_mant.checked_shr(exp_diff).unwrap_or(0);

    if sticky_bit {
        b_mant |= 1;
    }

    let mut mant = a_mant + b_mant;

    let overflow = (mant >> 26) != 0;
    if !overflow {
        if mant & 0b11 == 0b11 {
            mant += 0b100;
            if (mant >> 26) != 0 {
                mant >>= 1;
                a_exp += 1;
            }
        } else if mant & 0b110 == 0b110 {
            mant += 0b100;
            if (mant >> 26) != 0 {
                mant >>= 1;
                a_exp += 1;
            }
        }
    } else {
        match mant & 0b111 {
            0b111 | 0b110 | 0b101 => {
                mant += 0b1000;
            },
            0b100 => {
                if mant & 0b1000 == 0b1000 {
                    mant += 0b1000;
                }
            },
            _ => {},
        }
        
        mant >>= 1;
        a_exp += 1;
    }

    mant >>= 2;

    if mant >> 23 == 0 {
        a_exp = 0;
    } else {
        mant <<= 9;
        mant >>= 9;
    }
    
    f32::from_bits(mant | (a_exp << 23))
}
```
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

I have tested it for a quite a few different cases but I'm not sure how I can efficiently test it for all floating point numbers without it taking ages.

I would use something like the quickcheck crate (a port of Haskell's QuickCheck) to test the property of whether your addition function has the same results as ordinary f32 addition. If you don't know what quickcheck is, then this video might help.

Fuzzing your function as described in this video about Fuzz-Driven Development (FDD) might be another option.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.