Floating point multiplication #
For floating point, multiplication is easier than addition, as the exponent adds.
If z = x * y, we have
z.n * 2^(z.s - 2^63) = x.n * y.n * 2^(x.s + y.s - 2^63 - 2^63)
Auxiliary lemmas #
The limit for our 64 bit → 128 bit multiplication
Equations
- Floating.mul_n_max = (2 ^ 63 - 1) ^ 2
Instances For
Normalization of the inner integer multiplication #
theorem
Floating.mul_norm_correct
(n : UInt128)
(up : Bool)
(n0 : n ≠ 0)
(lo : n.toNat ≤ mul_n_max)
:
mul_norm returns a normalized, correctly rounded value.
I think writing a bunch of separate lemmas in Add.lean made the result super long.
Part of that is that the situation was very non-modular, as the component routine
semantics were very coupled. Here in Mul.lean things are hopefully better factored,
and ideally that means only mul_norm needs to know the internals of the mul_norm proof.
The definition of multiplication #
@[irreducible, inline]
Twos complement, 128-bit exponent e = xs + ys + t + 64 - 2^63
Equations
- Floating.mul_exponent xs ys t = ↑xs + ↑ys - ↑t - Int128.ofNat (2 ^ 63 - 64)
Instances For
Multiplication is correct #
mul_exponent is correct
theorem
Floating.approx_mul_of_nonneg
{x' y' : ℝ}
{x y : Floating}
{up : Bool}
{x0 : 0 ≤ x.val}
{y0 : 0 ≤ y.val}
(ax : approx x x')
(ay : approx y y')
:
Rounds (x.mul_of_nonneg y up x0 y0) (x' * y') up
mul_of_nonneg rounds in the correct direction