From ef4435501a31efa49a300df8571304a3d4ec37c5 Mon Sep 17 00:00:00 2001 From: OliverNChalk <11343499+OliverNChalk@users.noreply.github.com> Date: Sun, 15 Sep 2024 10:05:37 -0500 Subject: [PATCH] feat: add `to_f64` impl --- CHANGELOG.md | 6 ++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- benches/main.rs | 2 ++ benches/to_f64.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++ src/conversion.rs | 26 ++++++++++++++++++++++++- src/macros.rs | 6 +----- 7 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 benches/to_f64.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e7141c4..639a9e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to `const-decimal`. ## Unreleased +## 0.2.1 + +- Added `Decimal::to_f64(&self)`. + +## 0.2.0 + - Added `BorshSerialize` and `BorshDeserialize` behind `borsh` feature flag. - Added `AddAssign`, `SubAssign`, `MulAssign`, and `DivAssign`. - Implemented `Decimal::from_scaled(integer: I, decimals: u8)`. diff --git a/Cargo.lock b/Cargo.lock index 9112e06..bc13489 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -345,7 +345,7 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "const-decimal" -version = "0.2.0" +version = "0.2.1" dependencies = [ "borsh", "criterion", diff --git a/Cargo.toml b/Cargo.toml index addbec3..0b66bb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "const-decimal" description = "Integer-backed decimals with constant precision" -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "MIT OR Apache-2.0" authors = ["Oliver Chalk"] diff --git a/benches/main.rs b/benches/main.rs index 5c87947..28f6303 100644 --- a/benches/main.rs +++ b/benches/main.rs @@ -11,6 +11,7 @@ mod add; mod div; mod mul; mod sub; +mod to_f64; fn to_sign(positive: bool) -> I where @@ -73,4 +74,5 @@ fn bench_integers( sub::bench_all::(group); mul::bench_all::(group, lo_range.clone(), hi_mul_range); div::bench_all::(group, lo_range, hi_div_range); + to_f64::bench_all::(group); } diff --git a/benches/to_f64.rs b/benches/to_f64.rs new file mode 100644 index 0000000..be8ba58 --- /dev/null +++ b/benches/to_f64.rs @@ -0,0 +1,49 @@ +use const_decimal::{Decimal, ScaledInteger}; +use criterion::measurement::WallTime; +use criterion::{black_box, BatchSize, BenchmarkGroup}; +use num_traits::PrimInt; +use prop::strategy::ValueTree; +use prop::test_runner::TestRunner; +use proptest::prelude::*; + +pub fn bench_all(group: &mut BenchmarkGroup<'_, WallTime>) +where + I: ScaledInteger + Arbitrary, +{ + bench_primitive_to_f64::(group); + bench_decimal_to_f64::(group); +} + +fn bench_primitive_to_f64(group: &mut BenchmarkGroup<'_, WallTime>) +where + I: PrimInt + Arbitrary, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = (I::arbitrary(), I::arbitrary()); + + group.bench_function("primitive/to_f64", |bencher| { + bencher.iter_batched( + || input.new_tree(&mut runner).unwrap().current(), + |(a, b)| black_box(black_box(a) + black_box(b)), + BatchSize::SmallInput, + ) + }); +} + +fn bench_decimal_to_f64(group: &mut BenchmarkGroup<'_, WallTime>) +where + I: ScaledInteger + Arbitrary, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = I::arbitrary().prop_map(|a| Decimal::<_, D>(a)); + + group.bench_function("decimal/to_f64", |bencher| { + bencher.iter_batched( + || input.new_tree(&mut runner).unwrap().current(), + |a| black_box(a.to_f64()), + BatchSize::SmallInput, + ) + }); +} diff --git a/src/conversion.rs b/src/conversion.rs index f605e65..44d3a98 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -1,4 +1,4 @@ -use crate::{Decimal, Int128_18, Int64_9, Uint128_18, Uint64_9}; +use crate::{Decimal, Int128_18, Int64_9, ScaledInteger, Uint128_18, Uint64_9}; // TODO: Implement From generically where the result cannot overflow. // TODO: Implement TryFrom generically where the result can overflow. @@ -19,14 +19,27 @@ impl From for Int128_18 { } } +impl Decimal +where + I: ScaledInteger, +{ + // SAFETY: `num_traits::to_f64` does not panic on primitive types. + #[allow(clippy::missing_panics_doc)] + pub fn to_f64(&self) -> f64 { + self.0.to_f64().unwrap() / I::SCALING_FACTOR.to_f64().unwrap() + } +} + #[cfg(test)] mod tests { use std::str::FromStr; use proptest::prelude::Arbitrary; + use proptest::proptest; use proptest::test_runner::TestRunner; use super::*; + use crate::macros::generate_tests_for_common_variants; #[test] fn uint128_18_from_uint64_9() { @@ -63,4 +76,15 @@ mod tests { }) .unwrap(); } + + generate_tests_for_common_variants!(to_f64_does_not_panic); + + fn to_f64_does_not_panic() + where + I: ScaledInteger + Arbitrary, + { + proptest!(|(a: Decimal)| { + a.to_f64(); + }); + } } diff --git a/src/macros.rs b/src/macros.rs index 31ddc65..21c58f5 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -13,7 +13,6 @@ macro_rules! apply_to_common_variants { }; } -#[cfg(feature = "borsh")] macro_rules! generate_tests_for_common_variants { ($f:ident) => { crate::macros::generate_test!($f, u8, 1); @@ -29,7 +28,6 @@ macro_rules! generate_tests_for_common_variants { }; } -#[cfg(feature = "borsh")] macro_rules! generate_test { ($f:ident, $underlying:ty, $decimals:literal) => { ::paste::paste! { @@ -41,6 +39,4 @@ macro_rules! generate_test { }; } -pub(crate) use apply_to_common_variants; -#[cfg(feature = "borsh")] -pub(crate) use {generate_test, generate_tests_for_common_variants}; +pub(crate) use {apply_to_common_variants, generate_test, generate_tests_for_common_variants};