psychopath/sub_crates/rmath/src/lib.rs

113 lines
2.6 KiB
Rust

//! RMath: a math library for building CPU-based renderers.
#![allow(dead_code)]
mod normal;
mod point;
mod vector;
pub mod wide4;
mod xform;
use std::ops::{Add, Mul, Neg, Sub};
pub use self::{normal::Normal, point::Point, vector::Vector, xform::Xform, xform::XformFull};
/// Trait for calculating dot products.
pub trait DotProduct {
fn dot(self, other: Self) -> f32;
fn dot_fast(self, other: Self) -> f32;
}
#[inline(always)]
pub fn dot<T: DotProduct>(a: T, b: T) -> f32 {
a.dot(b)
}
#[inline(always)]
pub fn dot_fast<T: DotProduct>(a: T, b: T) -> f32 {
a.dot_fast(b)
}
/// Trait for calculating cross products.
pub trait CrossProduct {
fn cross(self, other: Self) -> Self;
fn cross_fast(self, other: Self) -> Self;
}
#[inline(always)]
pub fn cross<T: CrossProduct>(a: T, b: T) -> T {
a.cross(b)
}
#[inline(always)]
pub fn cross_fast<T: CrossProduct>(a: T, b: T) -> T {
a.cross_fast(b)
}
//-------------------------------------------------------------
/// Trait representing types that can do fused multiply-add.
trait FMulAdd {
/// `(self * b) + c` with only one floating point rounding error.
fn fma(self, b: Self, c: Self) -> Self;
}
impl FMulAdd for f32 {
fn fma(self, b: Self, c: Self) -> Self {
self.mul_add(b, c)
}
}
/// `(a * b) - (c * d)` but done with high precision via floating point tricks.
///
/// See https://pharr.org/matt/blog/2019/11/03/difference-of-floats
#[inline(always)]
fn difference_of_products<T>(a: T, b: T, c: T, d: T) -> T
where
T: Copy + FMulAdd + Add<Output = T> + Mul<Output = T> + Neg<Output = T>,
{
let cd = c * d;
let dop = a.fma(b, -cd);
let err = (-c).fma(d, cd);
dop + err
}
/// `(a * b) + (c * d)` but done with high precision via floating point tricks.
#[inline(always)]
fn sum_of_products<T>(a: T, b: T, c: T, d: T) -> T
where
T: Copy + FMulAdd + Add<Output = T> + Mul<Output = T> + Neg<Output = T>,
{
let cd = c * d;
let sop = a.fma(b, cd);
let err = c.fma(d, -cd);
sop + err
}
/// `a * b` but also returns a rounding error for precise composition
/// with other operations.
#[inline(always)]
fn two_prod<T>(a: T, b: T) -> (T, T)
// (product, rounding_err)
where
T: Copy + FMulAdd + Mul<Output = T> + Neg<Output = T>,
{
let ab = a * b;
(ab, a.fma(b, -ab))
}
/// `a + b` but also returns a rounding error for precise composition
/// with other operations.
#[inline(always)]
fn two_sum<T>(a: T, b: T) -> (T, T)
// (sum, rounding_err)
where
T: Copy + Add<Output = T> + Sub<Output = T>,
{
let sum = a + b;
let delta = sum - a;
(sum, (a - (sum - delta)) + (b - delta))
}