fixnum 0.9.5

Fixed-point numbers with explicit rounding
Documentation
use std::time::Instant;

use criterion::{black_box, criterion_group, criterion_main, Criterion};

use fixnum::{ops::*, FixedPoint};

#[cfg(feature = "i64")]
type F64p9 = FixedPoint<i64, typenum::U9>;
#[cfg(feature = "i128")]
type F128p18 = FixedPoint<i128, typenum::U18>;

macro_rules! define_bench {
    ($fp:tt) => {
        #[allow(non_snake_case)]
        fn $fp(c: &mut Criterion) {
            let mut group = c.benchmark_group(stringify!($fp));

            group.bench_function("cadd (~1e4)", |b| {
                let lhs = black_box($fp::try_from(12345i32).unwrap());
                let rhs = black_box($fp::try_from(54321i32).unwrap());
                b.iter(move || lhs.cadd(rhs))
            });

            let mut rmul = |mode| {
                group.bench_function(format!("rmul (~1e4, {:?})", mode), |b| {
                    let lhs = black_box(
                        $fp::try_from(12345i32)
                            .unwrap()
                            .cadd($fp::from_bits(1))
                            .unwrap(),
                    );
                    let rhs = black_box($fp::from_decimal(5, -1).unwrap());
                    b.iter(move || lhs.rmul(rhs, mode))
                });
            };

            rmul(RoundMode::Floor);
            rmul(RoundMode::Ceil);
            rmul(RoundMode::Nearest);

            let mut rdiv = |mode| {
                group.bench_function(format!("rdiv (~1e5/~1e4, {:?})", mode), |b| {
                    let lhs = black_box($fp::try_from(987656i32).unwrap());
                    let rhs = black_box($fp::try_from(54321i32).unwrap());
                    b.iter(move || lhs.rdiv(rhs, mode))
                });
            };

            rdiv(RoundMode::Floor);
            rdiv(RoundMode::Ceil);
            rdiv(RoundMode::Nearest);

            let mut rsqrt = |mode| {
                group.bench_function(format!("rsqrt (~1e4, {:?})", mode), |b| {
                    let x: $fp = black_box(22347.try_into().unwrap());
                    b.iter(move || x.rsqrt(mode))
                });
            };

            rsqrt(RoundMode::Floor);
            rsqrt(RoundMode::Ceil);
            rsqrt(RoundMode::Nearest);

            let mut rsqrt_max = |mode| {
                group.bench_function(format!("rsqrt (MAX, {:?})", mode), |b| {
                    let x = black_box($fp::MAX);
                    b.iter(move || x.rsqrt(mode))
                });
            };

            rsqrt_max(RoundMode::Floor);
            rsqrt_max(RoundMode::Ceil);
            rsqrt_max(RoundMode::Nearest);

            let mut rsqrt_adaptive = |mode| {
                group.bench_function(format!("rsqrt (adaptive, {:?})", mode), |b| {
                    b.iter_custom(|iters| {
                        let mut num = $fp::ZERO;
                        let step = $fp::MAX
                            .rdiv(*$fp::from_bits(iters as _).as_bits(), RoundMode::Floor)
                            .unwrap();

                        let started_time = Instant::now();
                        for _ in 0..iters {
                            num = num.cadd(step).unwrap();
                            let _ = black_box(num.rsqrt(mode));
                        }
                        started_time.elapsed()
                    })
                });
            };

            rsqrt_adaptive(RoundMode::Floor);
            rsqrt_adaptive(RoundMode::Ceil);
            rsqrt_adaptive(RoundMode::Nearest);

            group.bench_function("next_power_of_ten", |b| {
                let mut value = 0;

                b.iter(move || {
                    value += 1;
                    $fp::from_bits(value).next_power_of_ten()
                })
            });

            group.bench_function("try_from(f64) (MIN_POSITIVE)", |b| {
                let value = black_box(f64::MIN_POSITIVE);
                b.iter(move || $fp::try_from(value))
            });

            group.bench_function("try_from(f64) (MAX)", |b| {
                let value = black_box(f64::MAX);
                b.iter(move || $fp::try_from(value))
            });

            group.bench_function("try_from(f64) (~1e-12)", |b| {
                let value = black_box(3.141592653589793e-12);
                b.iter(move || $fp::try_from(value))
            });

            group.bench_function("try_from(f64) (~0.1)", |b| {
                let value = black_box(0.3141592653589793);
                b.iter(move || $fp::try_from(value))
            });

            group.bench_function("try_from(f64) (~1e6)", |b| {
                let value = black_box(3.141592653589793e6);
                b.iter(move || $fp::try_from(value))
            });

            group.bench_function("from_decimal(12345, -3)", |b| {
                let decimal = black_box((12345, -3));
                b.iter(move || $fp::from_decimal(decimal.0, decimal.1))
            });

            group.bench_function("to_decimal(0) (12.345)", |b| {
                let value = black_box($fp::from_decimal(12345, -3).unwrap());
                b.iter(move || value.to_decimal(0))
            });

            group.bench_function("to_decimal(i32::MAX) (12.345)", |b| {
                let value = black_box($fp::from_decimal(12345, -3).unwrap());
                b.iter(move || value.to_decimal(i32::MAX))
            });

            group.finish();
        }
    };
}

#[cfg(feature = "i64")]
define_bench!(F64p9);
#[cfg(feature = "i128")]
define_bench!(F128p18);

#[cfg(all(feature = "i64", feature = "i128"))]
criterion_group!(benches, F64p9, F128p18);
#[cfg(not(feature = "i128"))]
criterion_group!(benches, F64p9);
#[cfg(not(feature = "i64"))]
criterion_group!(benches, F128p18);

criterion_main!(benches);