diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09b5633..98a75b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: rust: [ - 1.97.0, # MSRV + 1.98.0, # MSRV stable, beta, nightly, @@ -52,7 +52,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.97.0 + - uses: dtolnay/rust-toolchain@1.98.0 with: components: rustfmt - run: cargo fmt --all --check diff --git a/Cargo.toml b/Cargo.toml index 34a8db1..e237893 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "num-primitive" -version = "0.3.9" +version = "0.3.10" description = "Traits for primitive numeric types" repository = "https://github.com/rust-num/num-primitive" license = "MIT OR Apache-2.0" keywords = ["generic", "mathematics", "numerics", "primitive"] categories = ["algorithms", "science", "no-std"] edition = "2024" -rust-version = "1.97" +rust-version = "1.98" [package.metadata.release] allow-branch = ["main"] diff --git a/README.md b/README.md index b1afb2c..d5992fe 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # num-primitive [![crate](https://img.shields.io/crates/v/num-primitive.svg)](https://crates.io/crates/num-primitive) -[![minimum rust 1.97](https://img.shields.io/badge/rust-1.97+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) +[![minimum rust 1.98](https://img.shields.io/badge/rust-1.98+-blue.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) [![documentation](https://docs.rs/num-primitive/badge.svg)](https://docs.rs/num-primitive) [![build status](https://github.com/rust-num/num-primitive/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-num/num-primitive/actions/workflows/ci.yml) diff --git a/RELEASES.md b/RELEASES.md index c9b694c..41ad34a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,11 @@ +# Release 0.3.10 (2026-08-20) + +- Updated to MSRV 1.98. +- Added `NonZeroPrimitiveInteger::from_str_radix`. +- Added `PrimitiveFloat::algebraic_{add,div,mul,rem,sub}` +- Added `PrimitiveInteger::{NumBuffer,format_into}`. +- Added `PrimitiveNumBuffer` to abstract `core::fmt::NumBuffer`. + # Release 0.3.9 (2026-07-09) - Updated to MSRV 1.97. diff --git a/src/float.rs b/src/float.rs index 29da3a0..543e831 100644 --- a/src/float.rs +++ b/src/float.rs @@ -186,6 +186,21 @@ pub trait PrimitiveFloat: /// Computes the absolute value of `self`. fn abs(self) -> Self; + /// Float addition that allows optimizations based on algebraic rules. + fn algebraic_add(self, rhs: Self) -> Self; + + /// Float division that allows optimizations based on algebraic rules. + fn algebraic_div(self, rhs: Self) -> Self; + + /// Float multiplication that allows optimizations based on algebraic rules. + fn algebraic_mul(self, rhs: Self) -> Self; + + /// Float remainder that allows optimizations based on algebraic rules. + fn algebraic_rem(self, rhs: Self) -> Self; + + /// Float subtraction that allows optimizations based on algebraic rules. + fn algebraic_sub(self, rhs: Self) -> Self; + /// Restrict a value to a certain interval unless it is NaN. fn clamp(self, min: Self, max: Self) -> Self; @@ -522,6 +537,11 @@ macro_rules! impl_float { } forward! { fn abs(self) -> Self; + fn algebraic_add(self, rhs: Self) -> Self; + fn algebraic_div(self, rhs: Self) -> Self; + fn algebraic_mul(self, rhs: Self) -> Self; + fn algebraic_rem(self, rhs: Self) -> Self; + fn algebraic_sub(self, rhs: Self) -> Self; fn clamp(self, min: Self, max: Self) -> Self; fn classify(self) -> FpCategory; fn copysign(self, sign: Self) -> Self; diff --git a/src/integer.rs b/src/integer.rs index 5cf6f66..f69f158 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -1,8 +1,9 @@ +use core::fmt::NumBuffer; use core::num::{NonZero, ParseIntError, TryFromIntError}; use crate::{PrimitiveError, PrimitiveNumber, PrimitiveNumberRef}; -trait NonZeroSealed {} +trait Sealed {} /// Trait for all primitive [integer types], including the supertrait [`PrimitiveNumber`]. /// @@ -195,9 +196,14 @@ pub trait PrimitiveInteger: { /// The non-zero integer type wrapping this primitive integer. /// - /// This is always `core::num::NonZero`. + /// This is always [`core::num::NonZero`]. type NonZero: NonZeroPrimitiveInteger; + /// The buffer type for [`format_into`][Self::format_into]. + /// + /// This is always [`core::fmt::NumBuffer`]. + type NumBuffer: PrimitiveNumBuffer; + /// The size of this integer type in bits. const BITS: u32; @@ -271,6 +277,9 @@ pub trait PrimitiveInteger: /// abs(rhs)`. fn div_euclid(self, rhs: Self) -> Self; + /// Writes `self` in decimal format into the given buffer, and returns it as a borrowed string. + fn format_into(self, buf: &mut Self::NumBuffer) -> &str; + /// Converts an integer from big endian to the target's endianness. fn from_be(value: Self) -> Self; @@ -643,7 +652,7 @@ pub trait PrimitiveIntegerRef: #[expect(private_bounds)] pub trait NonZeroPrimitiveInteger: 'static - + NonZeroSealed + + Sealed + core::cmp::Eq + core::cmp::Ord + core::convert::Into @@ -716,6 +725,9 @@ pub trait NonZeroPrimitiveInteger: /// Returns the number of ones in the binary representation of `self`. fn count_ones(self) -> NonZero; + /// Parses a non-zero integer from a string slice with digits in a given base. + fn from_str_radix(src: &str, radix: u32) -> Result; + /// Returns the contained value as a primitive type. fn get(self) -> Self::Integer; @@ -757,10 +769,61 @@ pub trait NonZeroPrimitiveInteger: unsafe fn new_unchecked(n: Self::Integer) -> Self; } +/// Trait for [`NumBuffer`] for the decimal formatting of a primitive integer type. +/// +/// In particular, this is used as a bound for the associated type [`PrimitiveInteger::NumBuffer`], +/// passed as an argument to the [`format_into`][`PrimitiveInteger::format_into`] method. The main +/// use for this trait is just to create a buffer with [`new`][Self::new]. +/// +/// This trait is sealed with a private trait to prevent downstream implementations, so we may +/// continue to expand along with the standard library without worrying about breaking changes for +/// implementors. +/// +/// # Examples +/// +/// ``` +/// use num_primitive::{PrimitiveInteger, PrimitiveNumBuffer}; +/// +/// fn check_format_into(x: T) { +/// assert!(size_of::() > size_of::()); +/// +/// let mut buf = T::NumBuffer::new(); +/// assert_eq!(x.format_into(&mut buf), x.to_string()); +/// +/// // Note that the buffer can be reused for multiple calls. +/// assert_eq!(T::default().format_into(&mut buf), "0"); +/// assert_eq!(T::as_from(1).format_into(&mut buf), "1"); +/// assert_eq!(T::MIN.format_into(&mut buf), T::MIN.to_string()); +/// assert_eq!(T::MAX.format_into(&mut buf), T::MAX.to_string()); +/// } +/// +/// check_format_into(123_u64); +/// check_format_into(-42_i32); +/// +/// assert!(size_of::<::NumBuffer>() +/// > size_of::<::NumBuffer>()); +/// ``` +#[expect(private_bounds)] +pub trait PrimitiveNumBuffer: + 'static + + Sealed + + core::fmt::Debug + + core::marker::Send + + core::marker::Sized + + core::marker::Sync + + core::marker::Unpin + + core::panic::RefUnwindSafe + + core::panic::UnwindSafe +{ + /// Creates a buffer. + fn new() -> Self; +} + macro_rules! impl_integer { ($($Integer:ident),*) => {$( impl PrimitiveInteger for $Integer { type NonZero = NonZero; + type NumBuffer = NumBuffer; use_consts!(Self::{ BITS: u32, @@ -791,6 +854,7 @@ macro_rules! impl_integer { fn count_ones(self) -> u32; fn count_zeros(self) -> u32; fn div_euclid(self, rhs: Self) -> Self; + fn format_into(self, buf: &mut Self::NumBuffer) -> &str; fn highest_one(self) -> Option; fn ilog(self, base: Self) -> u32; fn ilog10(self) -> u32; @@ -863,7 +927,7 @@ macro_rules! impl_integer { impl PrimitiveIntegerRef<$Integer> for &$Integer {} - impl NonZeroSealed for NonZero<$Integer> {} + impl Sealed for NonZero<$Integer> {} impl NonZeroPrimitiveInteger for NonZero<$Integer> { type Integer = $Integer; @@ -875,6 +939,7 @@ macro_rules! impl_integer { }); forward! { + fn from_str_radix(src: &str, radix: u32) -> Result; fn new(n: Self::Integer) -> Option; } forward! { @@ -895,6 +960,14 @@ macro_rules! impl_integer { unsafe fn new_unchecked(n: Self::Integer) -> Self; } } + + impl Sealed for NumBuffer<$Integer> {} + + impl PrimitiveNumBuffer for NumBuffer<$Integer> { + forward! { + fn new() -> Self; + } + } )*} } diff --git a/src/lib.rs b/src/lib.rs index a8ddf6c..1a66ca4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,9 @@ mod tests; pub use self::bytes::PrimitiveBytes; pub use self::error::PrimitiveError; pub use self::float::{PrimitiveFloat, PrimitiveFloatRef, PrimitiveFloatToInt}; -pub use self::integer::{NonZeroPrimitiveInteger, PrimitiveInteger, PrimitiveIntegerRef}; +pub use self::integer::{ + NonZeroPrimitiveInteger, PrimitiveInteger, PrimitiveIntegerRef, PrimitiveNumBuffer, +}; pub use self::number::{PrimitiveNumber, PrimitiveNumberAs, PrimitiveNumberRef}; pub use self::signed::{NonZeroPrimitiveSigned, PrimitiveSigned, PrimitiveSignedRef}; pub use self::unsigned::{NonZeroPrimitiveUnsigned, PrimitiveUnsigned, PrimitiveUnsignedRef};