Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
rust: [
1.97.0, # MSRV
1.98.0, # MSRV
stable,
beta,
nightly,
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
8 changes: 8 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -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<T>`.

# Release 0.3.9 (2026-07-09)

- Updated to MSRV 1.97.
Expand Down
20 changes: 20 additions & 0 deletions src/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down
81 changes: 77 additions & 4 deletions src/integer.rs
Original file line number Diff line number Diff line change
@@ -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`].
///
Expand Down Expand Up @@ -195,9 +196,14 @@ pub trait PrimitiveInteger:
{
/// The non-zero integer type wrapping this primitive integer.
///
/// This is always `core::num::NonZero<Self>`.
/// This is always [`core::num::NonZero<Self>`].
type NonZero: NonZeroPrimitiveInteger<Integer = Self>;

/// The buffer type for [`format_into`][Self::format_into].
///
/// This is always [`core::fmt::NumBuffer<Self>`].
type NumBuffer: PrimitiveNumBuffer;

/// The size of this integer type in bits.
const BITS: u32;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -643,7 +652,7 @@ pub trait PrimitiveIntegerRef<T>:
#[expect(private_bounds)]
pub trait NonZeroPrimitiveInteger:
'static
+ NonZeroSealed
+ Sealed
+ core::cmp::Eq
+ core::cmp::Ord
+ core::convert::Into<Self::Integer>
Expand Down Expand Up @@ -716,6 +725,9 @@ pub trait NonZeroPrimitiveInteger:
/// Returns the number of ones in the binary representation of `self`.
fn count_ones(self) -> NonZero<u32>;

/// Parses a non-zero integer from a string slice with digits in a given base.
fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError>;

/// Returns the contained value as a primitive type.
fn get(self) -> Self::Integer;

Expand Down Expand Up @@ -757,10 +769,61 @@ pub trait NonZeroPrimitiveInteger:
unsafe fn new_unchecked(n: Self::Integer) -> Self;
}

/// Trait for [`NumBuffer<T>`] 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<T: PrimitiveInteger>(x: T) {
/// assert!(size_of::<T::NumBuffer>() > size_of::<T>());
///
/// 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::<<u64 as PrimitiveInteger>::NumBuffer>()
/// > size_of::<<i32 as PrimitiveInteger>::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<Self>;
type NumBuffer = NumBuffer<Self>;

use_consts!(Self::{
BITS: u32,
Expand Down Expand Up @@ -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<u32>;
fn ilog(self, base: Self) -> u32;
fn ilog10(self) -> u32;
Expand Down Expand Up @@ -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;
Expand All @@ -875,6 +939,7 @@ macro_rules! impl_integer {
});

forward! {
fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError>;
fn new(n: Self::Integer) -> Option<Self>;
}
forward! {
Expand All @@ -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;
}
}
)*}
}

Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Loading