feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+1625
File diff suppressed because it is too large.
Load diff
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::{
|
||||
FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
|
||||
FixedU128,
|
||||
};
|
||||
use borsh::io::{Read, Result, Write};
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
|
||||
macro_rules! borsh_fixed {
|
||||
($Fixed:ident is $TBits:ident) => {
|
||||
impl<Frac> BorshSerialize for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
|
||||
<$TBits as BorshSerialize>::serialize(&self.bits, writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> BorshDeserialize for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
|
||||
<$TBits as BorshDeserialize>::deserialize_reader(reader).map($Fixed::from_bits)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
borsh_fixed! { FixedI8 is i8 }
|
||||
borsh_fixed! { FixedI16 is i16 }
|
||||
borsh_fixed! { FixedI32 is i32 }
|
||||
borsh_fixed! { FixedI64 is i64 }
|
||||
borsh_fixed! { FixedI128 is i128 }
|
||||
borsh_fixed! { FixedU8 is u8 }
|
||||
borsh_fixed! { FixedU16 is u16 }
|
||||
borsh_fixed! { FixedU32 is u32 }
|
||||
borsh_fixed! { FixedU64 is u64 }
|
||||
borsh_fixed! { FixedU128 is u128 }
|
||||
Vendored
+380
@@ -0,0 +1,380 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
// Kept trimmed: no underscores at beginning or end of slice
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct DigitsUnds<'a> {
|
||||
bytes: &'a [u8],
|
||||
digits: usize,
|
||||
}
|
||||
|
||||
impl<'a> DigitsUnds<'a> {
|
||||
pub const EMPTY: DigitsUnds<'a> = DigitsUnds::new(&[]);
|
||||
|
||||
pub const fn new(bytes: &'a [u8]) -> DigitsUnds<'a> {
|
||||
let mut digits = 0;
|
||||
let mut leading_unds = 0;
|
||||
let mut trailing_unds = 0;
|
||||
let mut rem_bytes = bytes;
|
||||
while let Some((&byte, rem)) = rem_bytes.split_first() {
|
||||
rem_bytes = rem;
|
||||
|
||||
if byte == b'_' {
|
||||
trailing_unds += 1;
|
||||
} else {
|
||||
if digits == 0 {
|
||||
leading_unds = trailing_unds;
|
||||
}
|
||||
digits += 1;
|
||||
trailing_unds = 0;
|
||||
}
|
||||
}
|
||||
let without_trailing_unds = bytes.split_at(bytes.len() - trailing_unds).0;
|
||||
let without_leading_unds = without_trailing_unds.split_at(leading_unds).1;
|
||||
DigitsUnds {
|
||||
bytes: without_leading_unds,
|
||||
digits,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn len(self) -> usize {
|
||||
self.digits
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn is_empty(self) -> bool {
|
||||
self.digits == 0
|
||||
}
|
||||
|
||||
pub const fn split_at(self, mid: usize) -> (DigitsUnds<'a>, DigitsUnds<'a>) {
|
||||
let mut remaining_digits = mid;
|
||||
let mut unds = 0;
|
||||
let mut rem_bytes = self.bytes;
|
||||
while let Some((&byte, rem)) = rem_bytes.split_first() {
|
||||
rem_bytes = rem;
|
||||
|
||||
if byte == b'_' {
|
||||
unds += 1;
|
||||
} else {
|
||||
remaining_digits -= 1;
|
||||
if remaining_digits == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if remaining_digits > 0 {
|
||||
panic!("index out of bounds");
|
||||
}
|
||||
let first = DigitsUnds {
|
||||
bytes: self.bytes.split_at(mid + unds).0,
|
||||
digits: mid,
|
||||
};
|
||||
|
||||
// skip over underscores between first part and last part
|
||||
while let Some((&b'_', rem)) = rem_bytes.split_first() {
|
||||
rem_bytes = rem;
|
||||
}
|
||||
(
|
||||
first,
|
||||
DigitsUnds {
|
||||
bytes: rem_bytes,
|
||||
digits: self.digits - mid,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn split_first(self) -> Option<(u8, DigitsUnds<'a>)> {
|
||||
let Some((&first, mut rem_bytes)) = self.bytes.split_first() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// first byte is never underscore
|
||||
debug_assert!(first != b'_');
|
||||
|
||||
// skip over underscores between first digit and last part
|
||||
while let Some((&b'_', rem)) = rem_bytes.split_first() {
|
||||
rem_bytes = rem;
|
||||
}
|
||||
Some((
|
||||
first,
|
||||
DigitsUnds {
|
||||
bytes: rem_bytes,
|
||||
digits: self.digits - 1,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn split_last(self) -> Option<(u8, DigitsUnds<'a>)> {
|
||||
let Some((&last, mut rem_bytes)) = self.bytes.split_last() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// last byte is never underscore
|
||||
debug_assert!(last != b'_');
|
||||
|
||||
// skip over underscores between first part and last digit
|
||||
while let Some((&b'_', rem)) = rem_bytes.split_last() {
|
||||
rem_bytes = rem;
|
||||
}
|
||||
Some((
|
||||
last,
|
||||
DigitsUnds {
|
||||
bytes: rem_bytes,
|
||||
digits: self.digits - 1,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
const fn split_leading_zeros(self) -> (usize, DigitsUnds<'a>) {
|
||||
let mut zeros = 0;
|
||||
let mut rem = self;
|
||||
while let Some((b'0', rest)) = rem.split_first() {
|
||||
zeros += 1;
|
||||
rem = rest;
|
||||
}
|
||||
(zeros, rem)
|
||||
}
|
||||
|
||||
const fn split_trailing_zeros(self) -> (usize, DigitsUnds<'a>) {
|
||||
let mut zeros = 0;
|
||||
let mut rem = self;
|
||||
while let Some((b'0', rest)) = rem.split_last() {
|
||||
zeros += 1;
|
||||
rem = rest;
|
||||
}
|
||||
(zeros, rem)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct DigitsExp<'a> {
|
||||
leading_zeros: usize,
|
||||
part1: DigitsUnds<'a>,
|
||||
part2: DigitsUnds<'a>,
|
||||
trailing_zeros: usize,
|
||||
}
|
||||
|
||||
impl<'a> DigitsExp<'a> {
|
||||
const EMPTY: DigitsExp<'a> = DigitsExp {
|
||||
leading_zeros: 0,
|
||||
part1: DigitsUnds::EMPTY,
|
||||
part2: DigitsUnds::EMPTY,
|
||||
trailing_zeros: 0,
|
||||
};
|
||||
|
||||
const fn new1(digits: DigitsUnds<'a>) -> DigitsExp<'a> {
|
||||
let (leading_zeros, rest) = digits.split_leading_zeros();
|
||||
let (trailing_zeros, rest) = rest.split_trailing_zeros();
|
||||
DigitsExp {
|
||||
leading_zeros,
|
||||
part1: rest,
|
||||
part2: DigitsUnds::EMPTY,
|
||||
trailing_zeros,
|
||||
}
|
||||
}
|
||||
|
||||
const fn new2(digits1: DigitsUnds<'a>, digits2: DigitsUnds<'a>) -> DigitsExp<'a> {
|
||||
let (mut leading_zeros, mut digits1) = digits1.split_leading_zeros();
|
||||
let digits2 = if digits1.is_empty() {
|
||||
let (more_leading_zeros, new_digits1) = digits2.split_leading_zeros();
|
||||
leading_zeros += more_leading_zeros;
|
||||
digits1 = new_digits1;
|
||||
DigitsUnds::EMPTY
|
||||
} else {
|
||||
digits2
|
||||
};
|
||||
let (mut trailing_zeros, digits2) = digits2.split_trailing_zeros();
|
||||
if digits2.is_empty() {
|
||||
let (more_trailing_zeros, new_digits1) = digits1.split_trailing_zeros();
|
||||
trailing_zeros += more_trailing_zeros;
|
||||
digits1 = new_digits1;
|
||||
}
|
||||
DigitsExp {
|
||||
leading_zeros,
|
||||
part1: digits1,
|
||||
part2: digits2,
|
||||
trailing_zeros,
|
||||
}
|
||||
}
|
||||
|
||||
// exp.unsigned_abs() must fit in usize, and results must have lengths that fit in usize
|
||||
pub const fn new_int_frac(
|
||||
int: DigitsUnds<'a>,
|
||||
frac: DigitsUnds<'a>,
|
||||
exp: i32,
|
||||
) -> Option<(DigitsExp<'a>, DigitsExp<'a>)> {
|
||||
if int.len() > usize::MAX - frac.len() {
|
||||
return None;
|
||||
}
|
||||
let abs_exp = exp.unsigned_abs() as usize;
|
||||
if abs_exp as u32 != exp.unsigned_abs() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (mut int, mut frac) = if exp == 0 {
|
||||
(DigitsExp::new1(int), DigitsExp::new1(frac))
|
||||
} else if exp < 0 {
|
||||
if let Some(extra_zeros) = abs_exp.checked_sub(int.len()) {
|
||||
let mut frac = DigitsExp::new2(int, frac);
|
||||
frac.trailing_zeros = 0;
|
||||
if extra_zeros > usize::MAX - frac.len() {
|
||||
return None;
|
||||
}
|
||||
frac.leading_zeros += extra_zeros;
|
||||
(DigitsExp::EMPTY, frac)
|
||||
} else {
|
||||
let int = int.split_at(int.len() - abs_exp);
|
||||
(DigitsExp::new1(int.0), DigitsExp::new2(int.1, frac))
|
||||
}
|
||||
} else {
|
||||
// exp > 0
|
||||
if let Some(extra_zeros) = abs_exp.checked_sub(frac.len()) {
|
||||
let mut int = DigitsExp::new2(int, frac);
|
||||
int.leading_zeros = 0;
|
||||
if extra_zeros > usize::MAX - int.len() {
|
||||
return None;
|
||||
}
|
||||
int.trailing_zeros += extra_zeros;
|
||||
(int, DigitsExp::EMPTY)
|
||||
} else {
|
||||
let frac = frac.split_at(abs_exp);
|
||||
(DigitsExp::new2(int, frac.0), DigitsExp::new1(frac.1))
|
||||
}
|
||||
};
|
||||
int.leading_zeros = 0;
|
||||
if int.part1.is_empty() && int.part2.is_empty() {
|
||||
int.trailing_zeros = 0;
|
||||
}
|
||||
frac.trailing_zeros = 0;
|
||||
if frac.part2.is_empty() && frac.part1.is_empty() {
|
||||
frac.leading_zeros = 0;
|
||||
}
|
||||
Some((int, frac))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn len(self) -> usize {
|
||||
self.leading_zeros + self.part1.len() + self.part2.len() + self.trailing_zeros
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn is_empty(self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub const fn split_at(self, mut mid: usize) -> (DigitsExp<'a>, DigitsExp<'a>) {
|
||||
let mut first = DigitsExp::EMPTY;
|
||||
let mut last = self;
|
||||
if mid == 0 {
|
||||
return (first, last);
|
||||
}
|
||||
|
||||
if mid < self.leading_zeros {
|
||||
(first.leading_zeros, last.leading_zeros) = (mid, self.leading_zeros - mid);
|
||||
return (first, last);
|
||||
}
|
||||
|
||||
(first.leading_zeros, last.leading_zeros) = (self.leading_zeros, 0);
|
||||
mid -= self.leading_zeros;
|
||||
if mid == 0 {
|
||||
return (first, last);
|
||||
}
|
||||
|
||||
if mid < self.part1.len() {
|
||||
(first.part1, last.part1) = self.part1.split_at(mid);
|
||||
return (first, last);
|
||||
}
|
||||
|
||||
first.part1 = self.part1;
|
||||
last.part1 = self.part2;
|
||||
last.part2 = DigitsUnds::EMPTY;
|
||||
mid -= self.part1.len();
|
||||
if mid == 0 {
|
||||
return (first, last);
|
||||
}
|
||||
|
||||
if mid < self.part2.len() {
|
||||
(first.part2, last.part1) = self.part2.split_at(mid);
|
||||
return (first, last);
|
||||
}
|
||||
|
||||
first.part2 = self.part2;
|
||||
last.leading_zeros = self.trailing_zeros;
|
||||
last.part1 = DigitsUnds::EMPTY;
|
||||
last.trailing_zeros = 0;
|
||||
mid -= self.part2.len();
|
||||
if mid == 0 {
|
||||
return (first, last);
|
||||
}
|
||||
|
||||
if mid < self.trailing_zeros {
|
||||
(first.trailing_zeros, last.leading_zeros) = (mid, self.trailing_zeros - mid);
|
||||
return (first, last);
|
||||
}
|
||||
|
||||
(first.trailing_zeros, last.leading_zeros) = (self.trailing_zeros, 0);
|
||||
mid -= self.trailing_zeros;
|
||||
if mid == 0 {
|
||||
return (first, last);
|
||||
}
|
||||
|
||||
panic!("index out of bounds");
|
||||
}
|
||||
|
||||
// no automatic renormalization done after split_first
|
||||
#[inline]
|
||||
pub const fn split_first(self) -> Option<(u8, DigitsExp<'a>)> {
|
||||
if self.leading_zeros > 0 {
|
||||
return Some((
|
||||
b'0',
|
||||
DigitsExp {
|
||||
leading_zeros: self.leading_zeros - 1,
|
||||
..self
|
||||
},
|
||||
));
|
||||
}
|
||||
if let Some((first, rest)) = self.part1.split_first() {
|
||||
return Some((
|
||||
first,
|
||||
DigitsExp {
|
||||
part1: rest,
|
||||
..self
|
||||
},
|
||||
));
|
||||
}
|
||||
if let Some((first, rest)) = self.part2.split_first() {
|
||||
return Some((
|
||||
first,
|
||||
DigitsExp {
|
||||
part2: rest,
|
||||
..self
|
||||
},
|
||||
));
|
||||
}
|
||||
if self.trailing_zeros > 0 {
|
||||
return Some((
|
||||
b'0',
|
||||
DigitsExp {
|
||||
trailing_zeros: self.trailing_zeros - 1,
|
||||
..self
|
||||
},
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
Vendored
+200
@@ -0,0 +1,200 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::types::extra::{LeEqU8, LeEqU16, LeEqU32, LeEqU64, LeEqU128};
|
||||
use crate::{
|
||||
F128, F128Bits, FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32,
|
||||
FixedU64, FixedU128,
|
||||
};
|
||||
use az::{Cast, CheckedCast, OverflowingCast, SaturatingCast, UnwrappedCast, WrappingCast};
|
||||
use half::{bf16 as half_bf16, f16 as half_f16};
|
||||
|
||||
macro_rules! cast {
|
||||
($Src:ident($LeEqUSrc:ident); $Dst:ident($LeEqUDst:ident)) => {
|
||||
impl<FracSrc: $LeEqUSrc, FracDst: $LeEqUDst> Cast<$Dst<FracDst>> for $Src<FracSrc> {
|
||||
#[inline]
|
||||
fn cast(self) -> $Dst<FracDst> {
|
||||
self.to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracSrc: $LeEqUSrc, FracDst: $LeEqUDst> CheckedCast<$Dst<FracDst>> for $Src<FracSrc> {
|
||||
#[inline]
|
||||
fn checked_cast(self) -> Option<$Dst<FracDst>> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracSrc: $LeEqUSrc, FracDst: $LeEqUDst> SaturatingCast<$Dst<FracDst>>
|
||||
for $Src<FracSrc>
|
||||
{
|
||||
#[inline]
|
||||
fn saturating_cast(self) -> $Dst<FracDst> {
|
||||
self.saturating_to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracSrc: $LeEqUSrc, FracDst: $LeEqUDst> WrappingCast<$Dst<FracDst>> for $Src<FracSrc> {
|
||||
#[inline]
|
||||
fn wrapping_cast(self) -> $Dst<FracDst> {
|
||||
self.wrapping_to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracSrc: $LeEqUSrc, FracDst: $LeEqUDst> OverflowingCast<$Dst<FracDst>>
|
||||
for $Src<FracSrc>
|
||||
{
|
||||
#[inline]
|
||||
fn overflowing_cast(self) -> ($Dst<FracDst>, bool) {
|
||||
self.overflowing_to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracSrc: $LeEqUSrc, FracDst: $LeEqUDst> UnwrappedCast<$Dst<FracDst>>
|
||||
for $Src<FracSrc>
|
||||
{
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn unwrapped_cast(self) -> $Dst<FracDst> {
|
||||
self.unwrapped_to_num()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($Fixed:ident($LeEqU:ident); $Dst:ident) => {
|
||||
impl<Frac: $LeEqU> Cast<$Dst> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn cast(self) -> $Dst {
|
||||
self.to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> CheckedCast<$Dst> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn checked_cast(self) -> Option<$Dst> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> SaturatingCast<$Dst> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn saturating_cast(self) -> $Dst {
|
||||
self.saturating_to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> WrappingCast<$Dst> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn wrapping_cast(self) -> $Dst {
|
||||
self.wrapping_to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> OverflowingCast<$Dst> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn overflowing_cast(self) -> ($Dst, bool) {
|
||||
self.overflowing_to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> UnwrappedCast<$Dst> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn unwrapped_cast(self) -> $Dst {
|
||||
self.unwrapped_to_num()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($Src:ident; $Fixed:ident($LeEqU:ident)) => {
|
||||
impl<Frac: $LeEqU> Cast<$Fixed<Frac>> for $Src {
|
||||
#[inline]
|
||||
fn cast(self) -> $Fixed<Frac> {
|
||||
<$Fixed<Frac>>::from_num(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> CheckedCast<$Fixed<Frac>> for $Src {
|
||||
#[inline]
|
||||
fn checked_cast(self) -> Option<$Fixed<Frac>> {
|
||||
<$Fixed<Frac>>::checked_from_num(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> SaturatingCast<$Fixed<Frac>> for $Src {
|
||||
#[inline]
|
||||
fn saturating_cast(self) -> $Fixed<Frac> {
|
||||
<$Fixed<Frac>>::saturating_from_num(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> WrappingCast<$Fixed<Frac>> for $Src {
|
||||
#[inline]
|
||||
fn wrapping_cast(self) -> $Fixed<Frac> {
|
||||
<$Fixed<Frac>>::wrapping_from_num(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> OverflowingCast<$Fixed<Frac>> for $Src {
|
||||
#[inline]
|
||||
fn overflowing_cast(self) -> ($Fixed<Frac>, bool) {
|
||||
<$Fixed<Frac>>::overflowing_from_num(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> UnwrappedCast<$Fixed<Frac>> for $Src {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn unwrapped_cast(self) -> $Fixed<Frac> {
|
||||
<$Fixed<Frac>>::unwrapped_from_num(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cast_num {
|
||||
($Src:ident($LeEqUSrc:ident); $($Dst:ident($LeEqUDst:ident),)*) => { $(
|
||||
cast! { $Src($LeEqUSrc); $Dst($LeEqUDst) }
|
||||
)* };
|
||||
($Fixed:ident($LeEqU:ident); $($Num:ident,)*) => { $(
|
||||
cast! { $Fixed($LeEqU); $Num }
|
||||
cast! { $Num; $Fixed($LeEqU) }
|
||||
)* };
|
||||
($($Fixed:ident($LeEqU:ident),)*) => { $(
|
||||
cast_num! {
|
||||
$Fixed($LeEqU);
|
||||
FixedI8(LeEqU8), FixedI16(LeEqU16), FixedI32(LeEqU32), FixedI64(LeEqU64),
|
||||
FixedI128(LeEqU128),
|
||||
FixedU8(LeEqU8), FixedU16(LeEqU16), FixedU32(LeEqU32), FixedU64(LeEqU64),
|
||||
FixedU128(LeEqU128),
|
||||
}
|
||||
cast! { bool; $Fixed($LeEqU) }
|
||||
cast_num! {
|
||||
$Fixed($LeEqU);
|
||||
i8, i16, i32, i64, i128, isize,
|
||||
u8, u16, u32, u64, u128, usize,
|
||||
half_f16, half_bf16, f32, f64, F128, F128Bits,
|
||||
}
|
||||
#[cfg(feature = "nightly-float")]
|
||||
cast_num! { $Fixed($LeEqU); f16, f128, }
|
||||
)* };
|
||||
}
|
||||
|
||||
cast_num! {
|
||||
FixedI8(LeEqU8), FixedI16(LeEqU16), FixedI32(LeEqU32), FixedI64(LeEqU64), FixedI128(LeEqU128),
|
||||
FixedU8(LeEqU8), FixedU16(LeEqU16), FixedU32(LeEqU32), FixedU64(LeEqU64), FixedU128(LeEqU128),
|
||||
}
|
||||
Vendored
+695
@@ -0,0 +1,695 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::float_helper;
|
||||
use crate::int_helper;
|
||||
use crate::int_helper::IntFixed;
|
||||
use crate::types::extra::Unsigned;
|
||||
use crate::{
|
||||
F128, F128Bits, FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32,
|
||||
FixedU64, FixedU128,
|
||||
};
|
||||
use core::cmp::Ordering;
|
||||
use core::ops::{Shl, Shr};
|
||||
use half::{bf16 as half_bf16, f16 as half_f16};
|
||||
|
||||
macro_rules! fixed_cmp_int {
|
||||
($Fixed:ident, $Int:ident) => {
|
||||
impl<Frac: Unsigned> PartialEq<$Int> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &$Int) -> bool {
|
||||
let fixed_rhs = IntFixed(*rhs).fixed();
|
||||
PartialEq::eq(self, &fixed_rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: Unsigned> PartialEq<$Fixed<Frac>> for $Int {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &$Fixed<Frac>) -> bool {
|
||||
let fixed_lhs = IntFixed(*self).fixed();
|
||||
PartialEq::eq(&fixed_lhs, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: Unsigned> PartialOrd<$Int> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &$Int) -> Option<Ordering> {
|
||||
let fixed_rhs = IntFixed(*rhs).fixed();
|
||||
PartialOrd::partial_cmp(self, &fixed_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lt(&self, rhs: &$Int) -> bool {
|
||||
let fixed_rhs = IntFixed(*rhs).fixed();
|
||||
PartialOrd::lt(self, &fixed_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn le(&self, rhs: &$Int) -> bool {
|
||||
let fixed_rhs = IntFixed(*rhs).fixed();
|
||||
PartialOrd::le(self, &fixed_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gt(&self, rhs: &$Int) -> bool {
|
||||
let fixed_rhs = IntFixed(*rhs).fixed();
|
||||
PartialOrd::gt(self, &fixed_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ge(&self, rhs: &$Int) -> bool {
|
||||
let fixed_rhs = IntFixed(*rhs).fixed();
|
||||
PartialOrd::ge(self, &fixed_rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: Unsigned> PartialOrd<$Fixed<Frac>> for $Int {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &$Fixed<Frac>) -> Option<Ordering> {
|
||||
let fixed_lhs = IntFixed(*self).fixed();
|
||||
PartialOrd::partial_cmp(&fixed_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lt(&self, rhs: &$Fixed<Frac>) -> bool {
|
||||
let fixed_lhs = IntFixed(*self).fixed();
|
||||
PartialOrd::lt(&fixed_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn le(&self, rhs: &$Fixed<Frac>) -> bool {
|
||||
let fixed_lhs = IntFixed(*self).fixed();
|
||||
PartialOrd::le(&fixed_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gt(&self, rhs: &$Fixed<Frac>) -> bool {
|
||||
let fixed_lhs = IntFixed(*self).fixed();
|
||||
PartialOrd::gt(&fixed_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ge(&self, rhs: &$Fixed<Frac>) -> bool {
|
||||
let fixed_lhs = IntFixed(*self).fixed();
|
||||
PartialOrd::ge(&fixed_lhs, rhs)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Zero must NOT be neg
|
||||
struct Value<U> {
|
||||
neg: bool,
|
||||
abs: U,
|
||||
bits: u32,
|
||||
frac_bits: i32,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// lhs_frac >= rhs_frac
|
||||
fn float_rhs_shl<U>(rhs_abs: U, bits: u32, lhs_frac: i32, rhs_frac: i32) -> Option<U>
|
||||
where
|
||||
U: Copy + Eq + TryFrom<u32> + Shl<u32, Output = U> + Shr<u32, Output = U>,
|
||||
{
|
||||
debug_assert!(lhs_frac >= rhs_frac);
|
||||
let rhs_shl = lhs_frac.wrapping_sub(rhs_frac) as u32;
|
||||
let Ok(rhs_zero) = U::try_from(0u32) else {
|
||||
unreachable!();
|
||||
};
|
||||
if rhs_abs == rhs_zero {
|
||||
Some(rhs_zero)
|
||||
} else if rhs_shl >= bits {
|
||||
None
|
||||
} else {
|
||||
let shifted = rhs_abs << rhs_shl;
|
||||
if (shifted >> rhs_shl) == rhs_abs {
|
||||
Some(shifted)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn float_eq_even<U>(lhs: Value<U>, rhs: Value<U>) -> bool
|
||||
where
|
||||
U: Copy + Eq + TryFrom<u32> + Shl<u32, Output = U> + Shr<u32, Output = U>,
|
||||
{
|
||||
if lhs.frac_bits < rhs.frac_bits {
|
||||
return float_eq_even(rhs, lhs);
|
||||
}
|
||||
|
||||
if lhs.neg != rhs.neg {
|
||||
return false;
|
||||
}
|
||||
|
||||
// lhs.frac_bits >= rhs.frac_bits
|
||||
match float_rhs_shl(rhs.abs, rhs.bits, lhs.frac_bits, rhs.frac_bits) {
|
||||
None => false,
|
||||
Some(shifted_rhs_abs) => lhs.abs == shifted_rhs_abs,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn float_eq<Lhs, Rhs>(lhs: Value<Lhs>, rhs: Value<Rhs>) -> bool
|
||||
where
|
||||
Lhs: Copy + Eq + TryFrom<u32> + TryFrom<Rhs> + Shl<u32, Output = Lhs> + Shr<u32, Output = Lhs>,
|
||||
Rhs: Copy + Eq + TryFrom<u32> + TryFrom<Lhs> + Shl<u32, Output = Rhs> + Shr<u32, Output = Rhs>,
|
||||
{
|
||||
if lhs.bits >= rhs.bits {
|
||||
let Ok(rhs_abs) = Lhs::try_from(rhs.abs) else {
|
||||
unreachable!();
|
||||
};
|
||||
let rhs = Value {
|
||||
neg: rhs.neg,
|
||||
abs: rhs_abs,
|
||||
bits: lhs.bits,
|
||||
frac_bits: rhs.frac_bits,
|
||||
};
|
||||
float_eq_even(lhs, rhs)
|
||||
} else {
|
||||
let Ok(lhs_abs) = Rhs::try_from(lhs.abs) else {
|
||||
unreachable!();
|
||||
};
|
||||
let lhs = Value {
|
||||
neg: lhs.neg,
|
||||
abs: lhs_abs,
|
||||
bits: rhs.bits,
|
||||
frac_bits: lhs.frac_bits,
|
||||
};
|
||||
float_eq_even(lhs, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn float_cmp_even<U>(lhs: Value<U>, rhs: Value<U>) -> Ordering
|
||||
where
|
||||
U: Copy + Ord + TryFrom<u32> + Shl<u32, Output = U> + Shr<u32, Output = U>,
|
||||
{
|
||||
if lhs.frac_bits < rhs.frac_bits {
|
||||
return float_cmp_even(rhs, lhs).reverse();
|
||||
}
|
||||
|
||||
if !lhs.neg && rhs.neg {
|
||||
return Ordering::Greater;
|
||||
}
|
||||
if lhs.neg && !rhs.neg {
|
||||
return Ordering::Less;
|
||||
}
|
||||
|
||||
match float_rhs_shl(rhs.abs, rhs.bits, lhs.frac_bits, rhs.frac_bits) {
|
||||
None => {
|
||||
// rhs is so large it doesn't fit
|
||||
if lhs.neg {
|
||||
// both lhs and rhs are negative, and rhs is even more negative
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
}
|
||||
Some(shifted_rhs_abs) => {
|
||||
if lhs.neg {
|
||||
// both lhs are negative, so reverse order
|
||||
shifted_rhs_abs.cmp(&lhs.abs)
|
||||
} else {
|
||||
lhs.abs.cmp(&shifted_rhs_abs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn float_cmp<Lhs, Rhs>(lhs: Value<Lhs>, rhs: Value<Rhs>) -> Ordering
|
||||
where
|
||||
Lhs: Copy + Ord + TryFrom<u32> + TryFrom<Rhs> + Shl<u32, Output = Lhs> + Shr<u32, Output = Lhs>,
|
||||
Rhs: Copy + Ord + TryFrom<u32> + TryFrom<Lhs> + Shl<u32, Output = Rhs> + Shr<u32, Output = Rhs>,
|
||||
{
|
||||
if lhs.bits >= rhs.bits {
|
||||
let Ok(rhs_abs) = Lhs::try_from(rhs.abs) else {
|
||||
unreachable!();
|
||||
};
|
||||
let rhs = Value {
|
||||
neg: rhs.neg,
|
||||
abs: rhs_abs,
|
||||
bits: lhs.bits,
|
||||
frac_bits: rhs.frac_bits,
|
||||
};
|
||||
float_cmp_even(lhs, rhs)
|
||||
} else {
|
||||
let Ok(lhs_abs) = Rhs::try_from(lhs.abs) else {
|
||||
unreachable!();
|
||||
};
|
||||
let lhs = Value {
|
||||
neg: lhs.neg,
|
||||
abs: lhs_abs,
|
||||
bits: rhs.bits,
|
||||
frac_bits: lhs.frac_bits,
|
||||
};
|
||||
float_cmp_even(lhs, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! fixed_cmp_float {
|
||||
($Fix:ident($Inner:ident), $Float:ident, $FloatBits:ident) => {
|
||||
impl<Frac: Unsigned> PartialEq<$Float> for $Fix<Frac> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &$Float) -> bool {
|
||||
use float_helper::$Float::Kind;
|
||||
let (lhs_neg, lhs_abs) = int_helper::$Inner::neg_abs(self.to_bits());
|
||||
let lhs = Value {
|
||||
neg: lhs_neg,
|
||||
abs: lhs_abs,
|
||||
bits: $Inner::BITS,
|
||||
frac_bits: Frac::to_i32(),
|
||||
};
|
||||
let Kind::Finite {
|
||||
neg: rhs_neg,
|
||||
abs: rhs_abs,
|
||||
frac_bits: rhs_frac,
|
||||
} = float_helper::$Float::kind(*rhs)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let rhs = Value {
|
||||
neg: rhs_neg,
|
||||
abs: rhs_abs,
|
||||
bits: $FloatBits::BITS,
|
||||
frac_bits: rhs_frac,
|
||||
};
|
||||
|
||||
float_eq(lhs, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: Unsigned> PartialEq<$Fix<Frac>> for $Float {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &$Fix<Frac>) -> bool {
|
||||
rhs.eq(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: Unsigned> PartialOrd<$Float> for $Fix<Frac> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &$Float) -> Option<Ordering> {
|
||||
use float_helper::$Float::Kind;
|
||||
let (lhs_neg, lhs_abs) = int_helper::$Inner::neg_abs(self.to_bits());
|
||||
let lhs = Value {
|
||||
neg: lhs_neg,
|
||||
abs: lhs_abs,
|
||||
bits: $Inner::BITS,
|
||||
frac_bits: Frac::to_i32(),
|
||||
};
|
||||
let (rhs_neg, rhs_abs, rhs_frac) = match float_helper::$Float::kind(*rhs) {
|
||||
Kind::Finite {
|
||||
neg,
|
||||
abs,
|
||||
frac_bits,
|
||||
} => (neg, abs, frac_bits),
|
||||
Kind::Infinite { neg } => {
|
||||
return if neg {
|
||||
Some(Ordering::Greater)
|
||||
} else {
|
||||
Some(Ordering::Less)
|
||||
};
|
||||
}
|
||||
Kind::NaN => return None,
|
||||
};
|
||||
let rhs = Value {
|
||||
neg: rhs_neg,
|
||||
abs: rhs_abs,
|
||||
bits: $FloatBits::BITS,
|
||||
frac_bits: rhs_frac,
|
||||
};
|
||||
|
||||
Some(float_cmp(lhs, rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: Unsigned> PartialOrd<$Fix<Frac>> for $Float {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &$Fix<Frac>) -> Option<Ordering> {
|
||||
rhs.partial_cmp(self).map(Ordering::reverse)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! fixed_cmp_all {
|
||||
($Fix:ident($LeEqU:ident, $Inner:ident)) => {
|
||||
fixed_cmp_int! { $Fix, i8 }
|
||||
fixed_cmp_int! { $Fix, i16 }
|
||||
fixed_cmp_int! { $Fix, i32 }
|
||||
fixed_cmp_int! { $Fix, i64 }
|
||||
fixed_cmp_int! { $Fix, i128 }
|
||||
fixed_cmp_int! { $Fix, isize }
|
||||
fixed_cmp_int! { $Fix, u8 }
|
||||
fixed_cmp_int! { $Fix, u16 }
|
||||
fixed_cmp_int! { $Fix, u32 }
|
||||
fixed_cmp_int! { $Fix, u64 }
|
||||
fixed_cmp_int! { $Fix, u128 }
|
||||
fixed_cmp_int! { $Fix, usize }
|
||||
#[cfg(feature = "nightly-float")]
|
||||
fixed_cmp_float! { $Fix($Inner), f16, u16 }
|
||||
fixed_cmp_float! { $Fix($Inner), half_f16, u16 }
|
||||
fixed_cmp_float! { $Fix($Inner), half_bf16, u16 }
|
||||
fixed_cmp_float! { $Fix($Inner), f32, u32 }
|
||||
fixed_cmp_float! { $Fix($Inner), f64, u64 }
|
||||
#[cfg(feature = "nightly-float")]
|
||||
fixed_cmp_float! { $Fix($Inner), f128, u128 }
|
||||
fixed_cmp_float! { $Fix($Inner), F128, u128 }
|
||||
fixed_cmp_float! { $Fix($Inner), F128Bits, u128 }
|
||||
};
|
||||
}
|
||||
|
||||
fixed_cmp_all! { FixedI8(LeEqU8, i8) }
|
||||
fixed_cmp_all! { FixedI16(LeEqU16, i16) }
|
||||
fixed_cmp_all! { FixedI32(LeEqU32, i32) }
|
||||
fixed_cmp_all! { FixedI64(LeEqU64, i64) }
|
||||
fixed_cmp_all! { FixedI128(LeEqU128, i128) }
|
||||
fixed_cmp_all! { FixedU8(LeEqU8, u8) }
|
||||
fixed_cmp_all! { FixedU16(LeEqU16, u16) }
|
||||
fixed_cmp_all! { FixedU32(LeEqU32, u32) }
|
||||
fixed_cmp_all! { FixedU64(LeEqU64, u64) }
|
||||
fixed_cmp_all! { FixedU128(LeEqU128, u128) }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
use core::cmp::Ordering;
|
||||
|
||||
#[test]
|
||||
fn cmp_signed() {
|
||||
use core::cmp::Ordering::*;
|
||||
let neg1_16 = FixedI32::<types::extra::U16>::NEG_ONE;
|
||||
let neg1_20 = FixedI32::<types::extra::U20>::NEG_ONE;
|
||||
let mut a = neg1_16;
|
||||
let mut b = neg1_20;
|
||||
// a = ffff.0000 = -1, b = fff.00000 = -1
|
||||
assert!(a.eq(&b) && b.eq(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Equal));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Equal));
|
||||
assert_eq!(a, -1i8);
|
||||
assert_eq!(b, -1i128);
|
||||
a >>= 16;
|
||||
b >>= 16;
|
||||
// a = ffff.ffff = -2^-16, b = fff.ffff0 = -2^-16
|
||||
assert!(a.eq(&b) && b.eq(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Equal));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Equal));
|
||||
assert!(a < 0.0);
|
||||
assert_eq!(a.partial_cmp(&f32::INFINITY), Some(Less));
|
||||
assert!(a < f32::INFINITY);
|
||||
assert!(a != f32::INFINITY);
|
||||
assert_eq!(a.partial_cmp(&f32::NEG_INFINITY), Some(Greater));
|
||||
assert!(a > f32::NEG_INFINITY);
|
||||
assert_eq!(a, -(-16f32).exp2());
|
||||
assert!(a <= -(-16f32).exp2());
|
||||
assert!(a >= -(-16f32).exp2());
|
||||
assert!(a < (-16f32).exp2());
|
||||
assert_ne!(a, -0.75 * (-16f32).exp2());
|
||||
assert!(a < -0.75 * (-16f32).exp2());
|
||||
assert!(a <= -0.75 * (-16f32).exp2());
|
||||
assert!(a > -1.25 * (-16f32).exp2());
|
||||
assert!(a >= -1.25 * (-16f32).exp2());
|
||||
a >>= 1;
|
||||
b >>= 1;
|
||||
// a = ffff.ffff = -2^-16, b = fff.ffff8 = -2^-17
|
||||
assert!(a.ne(&b) && b.ne(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Less));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Greater));
|
||||
a = neg1_16 << 11;
|
||||
b = neg1_20 << 11;
|
||||
// a = f800.0000 = -2^11, b = 800.00000 = -2^11
|
||||
assert!(a.eq(&b) && b.eq(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Equal));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Equal));
|
||||
assert_eq!(a, -1i16 << 11);
|
||||
assert_eq!(b, -1i64 << 11);
|
||||
a <<= 1;
|
||||
b <<= 1;
|
||||
// a = f000.0000 = -2^-12, b = 000.00000 = 0
|
||||
assert!(a.ne(&b) && b.ne(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Less));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Greater));
|
||||
assert!(a < 1u8);
|
||||
assert_eq!(b, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_unsigned() {
|
||||
use core::cmp::Ordering::*;
|
||||
let one_16 = FixedU32::<types::extra::U16>::ONE;
|
||||
let one_20 = FixedU32::<types::extra::U20>::ONE;
|
||||
let mut a = one_16;
|
||||
let mut b = one_20;
|
||||
// a = 0001.0000 = 1, b = 001.00000 = 1
|
||||
assert!(a.eq(&b) && b.eq(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Equal));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Equal));
|
||||
assert_eq!(a, 1u8);
|
||||
assert_eq!(b, 1i128);
|
||||
a >>= 16;
|
||||
b >>= 16;
|
||||
// a = 0000.0001 = 2^-16, b = 000.00010 = 2^-16
|
||||
assert!(a.eq(&b) && b.eq(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Equal));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Equal));
|
||||
assert!(a > 0.0);
|
||||
assert_eq!(a.partial_cmp(&f32::INFINITY), Some(Less));
|
||||
assert!(a < f32::INFINITY);
|
||||
assert!(a != f32::INFINITY);
|
||||
assert_eq!(a.partial_cmp(&f32::NEG_INFINITY), Some(Greater));
|
||||
assert!(a > f32::NEG_INFINITY);
|
||||
assert_eq!(a, (-16f64).exp2());
|
||||
assert!(a <= (-16f64).exp2());
|
||||
assert!(a >= (-16f64).exp2());
|
||||
assert!(a > -(-16f64).exp2());
|
||||
assert_ne!(a, 0.75 * (-16f64).exp2());
|
||||
assert!(a > 0.75 * (-16f64).exp2());
|
||||
assert!(a >= 0.75 * (-16f64).exp2());
|
||||
assert!(a < 1.25 * (-16f64).exp2());
|
||||
assert!(a <= 1.25 * (-16f64).exp2());
|
||||
a >>= 1;
|
||||
b >>= 1;
|
||||
// a = 0000.0000 = 0, b = 000.00008 = 2^-17
|
||||
assert!(a.ne(&b) && b.ne(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Less));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Greater));
|
||||
a = one_16 << 11;
|
||||
b = one_20 << 11;
|
||||
// a = 0800.0000 = 2^11, b = 800.00000 = 2^11
|
||||
assert!(a.eq(&b) && b.eq(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Equal));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Equal));
|
||||
assert_eq!(a, 1i16 << 11);
|
||||
assert_eq!(b, 1u64 << 11);
|
||||
a <<= 1;
|
||||
b <<= 1;
|
||||
// a = 1000.0000 = 2^12, b = 000.00000 = 0
|
||||
assert!(a.ne(&b) && b.ne(&a));
|
||||
assert_eq!(a.partial_cmp(&b), Some(Greater));
|
||||
assert_eq!(b.partial_cmp(&a), Some(Less));
|
||||
assert!(a > -1i8);
|
||||
assert_eq!(a, 1i32 << 12);
|
||||
assert_eq!(b, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_i0() {
|
||||
use crate::types::*;
|
||||
assert_eq!(I0F32::checked_from_num(0.5), None);
|
||||
for &float in &[
|
||||
-0.5,
|
||||
-0.5 + f32::EPSILON,
|
||||
-0.25,
|
||||
-f32::EPSILON,
|
||||
0.0,
|
||||
f32::EPSILON,
|
||||
0.25,
|
||||
0.5 - f32::EPSILON,
|
||||
] {
|
||||
let fixed = I0F32::from_num(float);
|
||||
let half = U0F32::from_num(0.5);
|
||||
assert_eq!(fixed < half, float < 0.5, "{fixed} < {half}");
|
||||
assert_eq!(fixed <= half, float <= 0.5, "{fixed} <= {half}");
|
||||
assert_eq!(fixed == half, float == 0.5, "{fixed} == {half}");
|
||||
assert_eq!(fixed >= half, float >= 0.5, "{fixed} >= {half}");
|
||||
assert_eq!(fixed > half, float > 0.5, "{fixed} > {half}");
|
||||
assert_eq!(
|
||||
fixed.partial_cmp(&half),
|
||||
float.partial_cmp(&0.5),
|
||||
"{fixed}.partial_cmp(&{half})"
|
||||
);
|
||||
assert_eq!(half < fixed, fixed > half);
|
||||
assert_eq!(half <= fixed, fixed >= half);
|
||||
assert_eq!(half == fixed, fixed == half);
|
||||
assert_eq!(half >= fixed, fixed <= half);
|
||||
assert_eq!(half > fixed, fixed < half);
|
||||
assert_eq!(
|
||||
half.partial_cmp(&fixed),
|
||||
fixed.partial_cmp(&half).map(Ordering::reverse)
|
||||
);
|
||||
|
||||
let half = I1F31::from_num(0.5);
|
||||
assert_eq!(fixed < half, float < 0.5, "{fixed} < {half}");
|
||||
assert_eq!(fixed <= half, float <= 0.5, "{fixed} <= {half}");
|
||||
assert_eq!(fixed == half, float == 0.5, "{fixed} == {half}");
|
||||
assert_eq!(fixed >= half, float >= 0.5, "{fixed} >= {half}");
|
||||
assert_eq!(fixed > half, float > 0.5, "{fixed} > {half}");
|
||||
assert_eq!(
|
||||
fixed.partial_cmp(&half),
|
||||
float.partial_cmp(&0.5),
|
||||
"{fixed}.partial_cmp(&{half})"
|
||||
);
|
||||
assert_eq!(half < fixed, fixed > half);
|
||||
assert_eq!(half <= fixed, fixed >= half);
|
||||
assert_eq!(half == fixed, fixed == half);
|
||||
assert_eq!(half >= fixed, fixed <= half);
|
||||
assert_eq!(half > fixed, fixed < half);
|
||||
assert_eq!(
|
||||
half.partial_cmp(&fixed),
|
||||
fixed.partial_cmp(&half).map(Ordering::reverse)
|
||||
);
|
||||
|
||||
let half = 0.5f32;
|
||||
assert_eq!(fixed < half, float < 0.5, "{fixed} < {half}");
|
||||
assert_eq!(fixed <= half, float <= 0.5, "{fixed} <= {half}");
|
||||
assert_eq!(fixed == half, float == 0.5, "{fixed} == {half}");
|
||||
assert_eq!(fixed >= half, float >= 0.5, "{fixed} >= {half}");
|
||||
assert_eq!(fixed > half, float > 0.5, "{fixed} > {half}");
|
||||
assert_eq!(
|
||||
fixed.partial_cmp(&half),
|
||||
float.partial_cmp(&0.5),
|
||||
"{fixed}.partial_cmp(&{half})"
|
||||
);
|
||||
assert_eq!(half < fixed, fixed > half);
|
||||
assert_eq!(half <= fixed, fixed >= half);
|
||||
assert_eq!(half == fixed, fixed == half);
|
||||
assert_eq!(half >= fixed, fixed <= half);
|
||||
assert_eq!(half > fixed, fixed < half);
|
||||
assert_eq!(
|
||||
half.partial_cmp(&fixed),
|
||||
fixed.partial_cmp(&half).map(Ordering::reverse)
|
||||
);
|
||||
|
||||
let m1 = I32F0::from_num(-1.0);
|
||||
assert_eq!(fixed < m1, float < -1.0, "{fixed} < {m1}");
|
||||
assert_eq!(fixed <= m1, float <= -1.0, "{fixed} <= {m1}");
|
||||
assert_eq!(fixed == m1, float == -1.0, "{fixed} == {m1}");
|
||||
assert_eq!(fixed >= m1, float >= -1.0, "{fixed} >= {m1}");
|
||||
assert_eq!(fixed > m1, float > -1.0, "{fixed} > {m1}");
|
||||
assert_eq!(
|
||||
fixed.partial_cmp(&m1),
|
||||
float.partial_cmp(&-1.0),
|
||||
"{fixed}.partial_cmp(&{m1})"
|
||||
);
|
||||
assert_eq!(m1 < fixed, fixed > m1);
|
||||
assert_eq!(m1 <= fixed, fixed >= m1);
|
||||
assert_eq!(m1 == fixed, fixed == m1);
|
||||
assert_eq!(m1 >= fixed, fixed <= m1);
|
||||
assert_eq!(m1 > fixed, fixed < m1);
|
||||
assert_eq!(
|
||||
m1.partial_cmp(&fixed),
|
||||
fixed.partial_cmp(&m1).map(Ordering::reverse)
|
||||
);
|
||||
|
||||
let m1 = I1F31::from_num(-1.0);
|
||||
assert_eq!(fixed < m1, float < -1.0, "{fixed} < {m1}");
|
||||
assert_eq!(fixed <= m1, float <= -1.0, "{fixed} <= {m1}");
|
||||
assert_eq!(fixed == m1, float == -1.0, "{fixed} == {m1}");
|
||||
assert_eq!(fixed >= m1, float >= -1.0, "{fixed} >= {m1}");
|
||||
assert_eq!(fixed > m1, float > -1.0, "{fixed} > {m1}");
|
||||
assert_eq!(
|
||||
fixed.partial_cmp(&m1),
|
||||
float.partial_cmp(&-1.0),
|
||||
"{fixed}.partial_cmp(&{m1})"
|
||||
);
|
||||
assert_eq!(m1 < fixed, fixed > m1);
|
||||
assert_eq!(m1 <= fixed, fixed >= m1);
|
||||
assert_eq!(m1 == fixed, fixed == m1);
|
||||
assert_eq!(m1 >= fixed, fixed <= m1);
|
||||
assert_eq!(m1 > fixed, fixed < m1);
|
||||
assert_eq!(
|
||||
m1.partial_cmp(&fixed),
|
||||
fixed.partial_cmp(&m1).map(Ordering::reverse)
|
||||
);
|
||||
|
||||
let m1 = -1.0f32;
|
||||
assert_eq!(fixed < m1, float < -1.0, "{fixed} < {m1}");
|
||||
assert_eq!(fixed <= m1, float <= -1.0, "{fixed} <= {m1}");
|
||||
assert_eq!(fixed == m1, float == -1.0, "{fixed} == {m1}");
|
||||
assert_eq!(fixed >= m1, float >= -1.0, "{fixed} >= {m1}");
|
||||
assert_eq!(fixed > m1, float > -1.0, "{fixed} > {m1}");
|
||||
assert_eq!(
|
||||
fixed.partial_cmp(&m1),
|
||||
float.partial_cmp(&-1.0),
|
||||
"{fixed}.partial_cmp(&{m1})"
|
||||
);
|
||||
assert_eq!(m1 < fixed, fixed > m1);
|
||||
assert_eq!(m1 <= fixed, fixed >= m1);
|
||||
assert_eq!(m1 == fixed, fixed == m1);
|
||||
assert_eq!(m1 >= fixed, fixed <= m1);
|
||||
assert_eq!(m1 > fixed, fixed < m1);
|
||||
assert_eq!(
|
||||
m1.partial_cmp(&fixed),
|
||||
fixed.partial_cmp(&m1).map(Ordering::reverse)
|
||||
);
|
||||
|
||||
let mhalf = I1F31::from_num(-0.5);
|
||||
assert_eq!(fixed < mhalf, float < -0.5, "{fixed} < {mhalf}");
|
||||
assert_eq!(fixed <= mhalf, float <= -0.5, "{fixed} <= {mhalf}");
|
||||
assert_eq!(fixed == mhalf, float == -0.5, "{fixed} == {mhalf}");
|
||||
assert_eq!(fixed >= mhalf, float >= -0.5, "{fixed} >= {mhalf}");
|
||||
assert_eq!(fixed > mhalf, float > -0.5, "{fixed} > {mhalf}");
|
||||
assert_eq!(
|
||||
fixed.partial_cmp(&mhalf),
|
||||
float.partial_cmp(&-0.5),
|
||||
"{fixed}.partial_cmp(&{mhalf})"
|
||||
);
|
||||
assert_eq!(mhalf < fixed, fixed > mhalf);
|
||||
assert_eq!(mhalf <= fixed, fixed >= mhalf);
|
||||
assert_eq!(mhalf == fixed, fixed == mhalf);
|
||||
assert_eq!(mhalf >= fixed, fixed <= mhalf);
|
||||
assert_eq!(mhalf > fixed, fixed < mhalf);
|
||||
assert_eq!(
|
||||
mhalf.partial_cmp(&fixed),
|
||||
fixed.partial_cmp(&mhalf).map(Ordering::reverse)
|
||||
);
|
||||
|
||||
let mhalf = -0.5f32;
|
||||
assert_eq!(fixed < mhalf, float < -0.5, "{fixed} < {mhalf}");
|
||||
assert_eq!(fixed <= mhalf, float <= -0.5, "{fixed} <= {mhalf}");
|
||||
assert_eq!(fixed == mhalf, float == -0.5, "{fixed} == {mhalf}");
|
||||
assert_eq!(fixed >= mhalf, float >= -0.5, "{fixed} >= {mhalf}");
|
||||
assert_eq!(fixed > mhalf, float > -0.5, "{fixed} > {mhalf}");
|
||||
assert_eq!(
|
||||
fixed.partial_cmp(&mhalf),
|
||||
float.partial_cmp(&-0.5),
|
||||
"{fixed}.partial_cmp(&{mhalf})"
|
||||
);
|
||||
assert_eq!(mhalf < fixed, fixed > mhalf);
|
||||
assert_eq!(mhalf <= fixed, fixed >= mhalf);
|
||||
assert_eq!(mhalf == fixed, fixed == mhalf);
|
||||
assert_eq!(mhalf >= fixed, fixed <= mhalf);
|
||||
assert_eq!(mhalf > fixed, fixed < mhalf);
|
||||
assert_eq!(
|
||||
mhalf.partial_cmp(&fixed),
|
||||
fixed.partial_cmp(&mhalf).map(Ordering::reverse)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+498
@@ -0,0 +1,498 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::types::extra::Unsigned;
|
||||
use crate::{
|
||||
FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
|
||||
FixedU128,
|
||||
};
|
||||
use core::cmp::Ordering;
|
||||
|
||||
// Works by converting the signed number to unsigned, but does not change size
|
||||
// of either number.
|
||||
macro_rules! diff_sign {
|
||||
($Sig:ident($Uns:ident, $UnsInner:ident), $OtherUns:ident) => {
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialEq<$Sig<FracRhs>> for $OtherUns<FracLhs> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &$Sig<FracRhs>) -> bool {
|
||||
if rhs.is_negative() {
|
||||
return false;
|
||||
}
|
||||
let unsigned_rhs = $Uns::<FracRhs>::from_bits(rhs.to_bits() as $UnsInner);
|
||||
PartialEq::eq(self, &unsigned_rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialEq<$OtherUns<FracRhs>> for $Sig<FracLhs> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &$OtherUns<FracRhs>) -> bool {
|
||||
if self.is_negative() {
|
||||
return false;
|
||||
}
|
||||
let unsigned_lhs = $Uns::<FracLhs>::from_bits(self.to_bits() as $UnsInner);
|
||||
PartialEq::eq(&unsigned_lhs, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialOrd<$Sig<FracRhs>>
|
||||
for $OtherUns<FracLhs>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &$Sig<FracRhs>) -> Option<Ordering> {
|
||||
if rhs.is_negative() {
|
||||
return Some(Ordering::Greater);
|
||||
}
|
||||
let unsigned_rhs = $Uns::<FracRhs>::from_bits(rhs.to_bits() as $UnsInner);
|
||||
PartialOrd::partial_cmp(self, &unsigned_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lt(&self, rhs: &$Sig<FracRhs>) -> bool {
|
||||
if rhs.is_negative() {
|
||||
return false;
|
||||
}
|
||||
let unsigned_rhs = $Uns::<FracRhs>::from_bits(rhs.to_bits() as $UnsInner);
|
||||
PartialOrd::lt(self, &unsigned_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn le(&self, rhs: &$Sig<FracRhs>) -> bool {
|
||||
if rhs.is_negative() {
|
||||
return false;
|
||||
}
|
||||
let unsigned_rhs = $Uns::<FracRhs>::from_bits(rhs.to_bits() as $UnsInner);
|
||||
PartialOrd::le(self, &unsigned_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gt(&self, rhs: &$Sig<FracRhs>) -> bool {
|
||||
if rhs.is_negative() {
|
||||
return true;
|
||||
}
|
||||
let unsigned_rhs = $Uns::<FracRhs>::from_bits(rhs.to_bits() as $UnsInner);
|
||||
PartialOrd::gt(self, &unsigned_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ge(&self, rhs: &$Sig<FracRhs>) -> bool {
|
||||
if rhs.is_negative() {
|
||||
return true;
|
||||
}
|
||||
let unsigned_rhs = $Uns::<FracRhs>::from_bits(rhs.to_bits() as $UnsInner);
|
||||
PartialOrd::ge(self, &unsigned_rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialOrd<$OtherUns<FracRhs>>
|
||||
for $Sig<FracLhs>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &$OtherUns<FracRhs>) -> Option<Ordering> {
|
||||
if self.is_negative() {
|
||||
return Some(Ordering::Less);
|
||||
}
|
||||
let unsigned_lhs = $Uns::<FracLhs>::from_bits(self.to_bits() as $UnsInner);
|
||||
PartialOrd::partial_cmp(&unsigned_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lt(&self, rhs: &$OtherUns<FracRhs>) -> bool {
|
||||
if self.is_negative() {
|
||||
return true;
|
||||
}
|
||||
let unsigned_lhs = $Uns::<FracLhs>::from_bits(self.to_bits() as $UnsInner);
|
||||
PartialOrd::lt(&unsigned_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn le(&self, rhs: &$OtherUns<FracRhs>) -> bool {
|
||||
if self.is_negative() {
|
||||
return true;
|
||||
}
|
||||
let unsigned_lhs = $Uns::<FracLhs>::from_bits(self.to_bits() as $UnsInner);
|
||||
PartialOrd::le(&unsigned_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gt(&self, rhs: &$OtherUns<FracRhs>) -> bool {
|
||||
if self.is_negative() {
|
||||
return false;
|
||||
}
|
||||
let unsigned_lhs = $Uns::<FracLhs>::from_bits(self.to_bits() as $UnsInner);
|
||||
PartialOrd::gt(&unsigned_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ge(&self, rhs: &$OtherUns<FracRhs>) -> bool {
|
||||
if self.is_negative() {
|
||||
return false;
|
||||
}
|
||||
let unsigned_lhs = $Uns::<FracLhs>::from_bits(self.to_bits() as $UnsInner);
|
||||
PartialOrd::ge(&unsigned_lhs, rhs)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($Sig:ident($Uns:ident, $UnsInner:ident)) => {
|
||||
diff_sign! { $Sig($Uns, $UnsInner), FixedU8 }
|
||||
diff_sign! { $Sig($Uns, $UnsInner), FixedU16 }
|
||||
diff_sign! { $Sig($Uns, $UnsInner), FixedU32 }
|
||||
diff_sign! { $Sig($Uns, $UnsInner), FixedU64 }
|
||||
diff_sign! { $Sig($Uns, $UnsInner), FixedU128 }
|
||||
};
|
||||
}
|
||||
|
||||
diff_sign! { FixedI8(FixedU8, u8) }
|
||||
diff_sign! { FixedI16(FixedU16, u16) }
|
||||
diff_sign! { FixedI32(FixedU32, u32) }
|
||||
diff_sign! { FixedI64(FixedU64, u64) }
|
||||
diff_sign! { FixedI128(FixedU128, u128) }
|
||||
|
||||
// Both numbers must have the same sign (both signed or both unsigned). Works by
|
||||
// widening the narrow number to have the same width as the wide number.
|
||||
macro_rules! diff_size {
|
||||
($Nar:ident, $Wid:ident($WidInner:ident)) => {
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialEq<$Nar<FracRhs>> for $Wid<FracLhs> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &$Nar<FracRhs>) -> bool {
|
||||
let widened_rhs = $Wid::<FracRhs>::from_bits(rhs.to_bits() as $WidInner);
|
||||
PartialEq::eq(self, &widened_rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialEq<$Wid<FracRhs>> for $Nar<FracLhs> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &$Wid<FracRhs>) -> bool {
|
||||
let widened_lhs = $Wid::<FracLhs>::from_bits(self.to_bits() as $WidInner);
|
||||
PartialEq::eq(&widened_lhs, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialOrd<$Nar<FracRhs>> for $Wid<FracLhs> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &$Nar<FracRhs>) -> Option<Ordering> {
|
||||
let widened_rhs = $Wid::<FracRhs>::from_bits(rhs.to_bits() as $WidInner);
|
||||
PartialOrd::partial_cmp(self, &widened_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lt(&self, rhs: &$Nar<FracRhs>) -> bool {
|
||||
let widened_rhs = $Wid::<FracRhs>::from_bits(rhs.to_bits() as $WidInner);
|
||||
PartialOrd::lt(self, &widened_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn le(&self, rhs: &$Nar<FracRhs>) -> bool {
|
||||
let widened_rhs = $Wid::<FracRhs>::from_bits(rhs.to_bits() as $WidInner);
|
||||
PartialOrd::le(self, &widened_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gt(&self, rhs: &$Nar<FracRhs>) -> bool {
|
||||
let widened_rhs = $Wid::<FracRhs>::from_bits(rhs.to_bits() as $WidInner);
|
||||
PartialOrd::gt(self, &widened_rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ge(&self, rhs: &$Nar<FracRhs>) -> bool {
|
||||
let widened_rhs = $Wid::<FracRhs>::from_bits(rhs.to_bits() as $WidInner);
|
||||
PartialOrd::ge(self, &widened_rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialOrd<$Wid<FracRhs>> for $Nar<FracLhs> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &$Wid<FracRhs>) -> Option<Ordering> {
|
||||
let widened_lhs = $Wid::<FracLhs>::from_bits(self.to_bits() as $WidInner);
|
||||
PartialOrd::partial_cmp(&widened_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lt(&self, rhs: &$Wid<FracRhs>) -> bool {
|
||||
let widened_lhs = $Wid::<FracLhs>::from_bits(self.to_bits() as $WidInner);
|
||||
PartialOrd::lt(&widened_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn le(&self, rhs: &$Wid<FracRhs>) -> bool {
|
||||
let widened_lhs = $Wid::<FracLhs>::from_bits(self.to_bits() as $WidInner);
|
||||
PartialOrd::le(&widened_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gt(&self, rhs: &$Wid<FracRhs>) -> bool {
|
||||
let widened_lhs = $Wid::<FracLhs>::from_bits(self.to_bits() as $WidInner);
|
||||
PartialOrd::gt(&widened_lhs, rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ge(&self, rhs: &$Wid<FracRhs>) -> bool {
|
||||
let widened_lhs = $Wid::<FracLhs>::from_bits(self.to_bits() as $WidInner);
|
||||
PartialOrd::ge(&widened_lhs, rhs)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
diff_size! { FixedI8, FixedI16(i16) }
|
||||
diff_size! { FixedI8, FixedI32(i32) }
|
||||
diff_size! { FixedI8, FixedI64(i64) }
|
||||
diff_size! { FixedI8, FixedI128(i128) }
|
||||
diff_size! { FixedI16, FixedI32(i32) }
|
||||
diff_size! { FixedI16, FixedI64(i64) }
|
||||
diff_size! { FixedI16, FixedI128(i128) }
|
||||
diff_size! { FixedI32, FixedI64(i64) }
|
||||
diff_size! { FixedI32, FixedI128(i128) }
|
||||
diff_size! { FixedI64, FixedI128(i128) }
|
||||
diff_size! { FixedU8, FixedU16(u16) }
|
||||
diff_size! { FixedU8, FixedU32(u32) }
|
||||
diff_size! { FixedU8, FixedU64(u64) }
|
||||
diff_size! { FixedU8, FixedU128(u128) }
|
||||
diff_size! { FixedU16, FixedU32(u32) }
|
||||
diff_size! { FixedU16, FixedU64(u64) }
|
||||
diff_size! { FixedU16, FixedU128(u128) }
|
||||
diff_size! { FixedU32, FixedU64(u64) }
|
||||
diff_size! { FixedU32, FixedU128(u128) }
|
||||
diff_size! { FixedU64, FixedU128(u128) }
|
||||
|
||||
macro_rules! cmp {
|
||||
($Fixed:ident($Inner:ident)) => {
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialEq<$Fixed<FracRhs>> for $Fixed<FracLhs> {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &$Fixed<FracRhs>) -> bool {
|
||||
let lhs = self.to_bits();
|
||||
let rhs = rhs.to_bits();
|
||||
|
||||
// lhs_extra_frac and nbits are known exactly at compile time,
|
||||
// so with optimizations the branch is selected at compile time
|
||||
let lhs_extra_frac = FracLhs::to_i32().saturating_sub(FracRhs::to_i32());
|
||||
let nbits = $Inner::BITS as i32;
|
||||
|
||||
if lhs_extra_frac <= -nbits {
|
||||
let shifted_rhs = rhs >> (nbits - 1) >> 1;
|
||||
let rhs_is_reduced = rhs != 0;
|
||||
lhs == shifted_rhs && !rhs_is_reduced
|
||||
} else if lhs_extra_frac < 0 {
|
||||
let shifted_rhs = rhs >> -lhs_extra_frac;
|
||||
let rhs_is_reduced = rhs != (shifted_rhs << -lhs_extra_frac);
|
||||
lhs == shifted_rhs && !rhs_is_reduced
|
||||
} else if lhs_extra_frac == 0 {
|
||||
lhs == rhs
|
||||
} else if lhs_extra_frac < nbits {
|
||||
let shifted_lhs = lhs >> lhs_extra_frac;
|
||||
let lhs_is_reduced = lhs != (shifted_lhs << lhs_extra_frac);
|
||||
shifted_lhs == rhs && !lhs_is_reduced
|
||||
} else {
|
||||
let shifted_lhs = lhs >> (nbits - 1) >> 1;
|
||||
let lhs_is_reduced = lhs != 0;
|
||||
shifted_lhs == rhs && !lhs_is_reduced
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<FracLhs: Unsigned, FracRhs: Unsigned> PartialOrd<$Fixed<FracRhs>> for $Fixed<FracLhs> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &$Fixed<FracRhs>) -> Option<Ordering> {
|
||||
let lhs = self.to_bits();
|
||||
let rhs = rhs.to_bits();
|
||||
|
||||
// lhs_extra_frac and nbits are known exactly at compile time,
|
||||
// so with optimizations the branch is selected at compile time
|
||||
let lhs_extra_frac = FracLhs::to_i32().saturating_sub(FracRhs::to_i32());
|
||||
let nbits = $Inner::BITS as i32;
|
||||
if lhs_extra_frac <= -nbits {
|
||||
let shifted_rhs = rhs >> (nbits - 1) >> 1;
|
||||
let rhs_is_reduced = rhs != 0;
|
||||
if lhs == shifted_rhs && rhs_is_reduced {
|
||||
Some(Ordering::Less)
|
||||
} else {
|
||||
Some(Ord::cmp(&lhs, &shifted_rhs))
|
||||
}
|
||||
} else if lhs_extra_frac < 0 {
|
||||
let shifted_rhs = rhs >> -lhs_extra_frac;
|
||||
let rhs_is_reduced = rhs != (shifted_rhs << -lhs_extra_frac);
|
||||
if lhs == shifted_rhs && rhs_is_reduced {
|
||||
Some(Ordering::Less)
|
||||
} else {
|
||||
Some(Ord::cmp(&lhs, &shifted_rhs))
|
||||
}
|
||||
} else if lhs_extra_frac == 0 {
|
||||
Some(Ord::cmp(&lhs, &rhs))
|
||||
} else if lhs_extra_frac < nbits {
|
||||
let shifted_lhs = lhs >> lhs_extra_frac;
|
||||
let lhs_is_reduced = lhs != (shifted_lhs << lhs_extra_frac);
|
||||
if shifted_lhs == rhs && lhs_is_reduced {
|
||||
Some(Ordering::Greater)
|
||||
} else {
|
||||
Some(Ord::cmp(&shifted_lhs, &rhs))
|
||||
}
|
||||
} else {
|
||||
let shifted_lhs = lhs >> (nbits - 1) >> 1;
|
||||
let lhs_is_reduced = lhs != 0;
|
||||
if shifted_lhs == rhs && lhs_is_reduced {
|
||||
Some(Ordering::Greater)
|
||||
} else {
|
||||
Some(Ord::cmp(&shifted_lhs, &rhs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn lt(&self, rhs: &$Fixed<FracRhs>) -> bool {
|
||||
let lhs = self.to_bits();
|
||||
let rhs = rhs.to_bits();
|
||||
|
||||
// lhs_extra_frac and nbits are known exactly at compile time,
|
||||
// so with optimizations the branch is selected at compile time
|
||||
let lhs_extra_frac = FracLhs::to_i32().saturating_sub(FracRhs::to_i32());
|
||||
let nbits = $Inner::BITS as i32;
|
||||
if lhs_extra_frac <= -nbits {
|
||||
let shifted_rhs = rhs >> (nbits - 1) >> 1;
|
||||
let rhs_is_reduced = rhs != 0;
|
||||
(lhs == shifted_rhs && rhs_is_reduced) || lhs < shifted_rhs
|
||||
} else if lhs_extra_frac < 0 {
|
||||
let shifted_rhs = rhs >> -lhs_extra_frac;
|
||||
let rhs_is_reduced = rhs != (shifted_rhs << -lhs_extra_frac);
|
||||
(lhs == shifted_rhs && rhs_is_reduced) || lhs < shifted_rhs
|
||||
} else if lhs_extra_frac == 0 {
|
||||
lhs < rhs
|
||||
} else if lhs_extra_frac < nbits {
|
||||
let shifted_lhs = lhs >> lhs_extra_frac;
|
||||
shifted_lhs < rhs
|
||||
} else {
|
||||
let shifted_lhs = lhs >> (nbits - 1) >> 1;
|
||||
shifted_lhs < rhs
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn le(&self, rhs: &$Fixed<FracRhs>) -> bool {
|
||||
let lhs = self.to_bits();
|
||||
let rhs = rhs.to_bits();
|
||||
|
||||
// lhs_extra_frac and nbits are known exactly at compile time,
|
||||
// so with optimizations the branch is selected at compile time
|
||||
let lhs_extra_frac = FracLhs::to_i32().saturating_sub(FracRhs::to_i32());
|
||||
let nbits = $Inner::BITS as i32;
|
||||
if lhs_extra_frac <= -nbits {
|
||||
let shifted_rhs = rhs >> (nbits - 1) >> 1;
|
||||
lhs <= shifted_rhs
|
||||
} else if lhs_extra_frac < 0 {
|
||||
let shifted_rhs = rhs >> -lhs_extra_frac;
|
||||
lhs <= shifted_rhs
|
||||
} else if lhs_extra_frac == 0 {
|
||||
lhs <= rhs
|
||||
} else if lhs_extra_frac < nbits {
|
||||
let shifted_lhs = lhs >> lhs_extra_frac;
|
||||
let lhs_is_reduced = lhs != (shifted_lhs << lhs_extra_frac);
|
||||
!(shifted_lhs == rhs && lhs_is_reduced) && shifted_lhs <= rhs
|
||||
} else {
|
||||
let shifted_lhs = lhs >> (nbits - 1) >> 1;
|
||||
let lhs_is_reduced = lhs != 0;
|
||||
!(shifted_lhs == rhs && lhs_is_reduced) && shifted_lhs <= rhs
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gt(&self, rhs: &$Fixed<FracRhs>) -> bool {
|
||||
let lhs = self.to_bits();
|
||||
let rhs = rhs.to_bits();
|
||||
|
||||
// lhs_extra_frac and nbits are known exactly at compile time,
|
||||
// so with optimizations the branch is selected at compile time
|
||||
let lhs_extra_frac = FracLhs::to_i32().saturating_sub(FracRhs::to_i32());
|
||||
let nbits = $Inner::BITS as i32;
|
||||
if lhs_extra_frac <= -nbits {
|
||||
let shifted_rhs = rhs >> (nbits - 1) >> 1;
|
||||
lhs > shifted_rhs
|
||||
} else if lhs_extra_frac < 0 {
|
||||
let shifted_rhs = rhs >> -lhs_extra_frac;
|
||||
lhs > shifted_rhs
|
||||
} else if lhs_extra_frac == 0 {
|
||||
lhs > rhs
|
||||
} else if lhs_extra_frac < nbits {
|
||||
let shifted_lhs = lhs >> lhs_extra_frac;
|
||||
let lhs_is_reduced = lhs != (shifted_lhs << lhs_extra_frac);
|
||||
(shifted_lhs == rhs && lhs_is_reduced) || shifted_lhs > rhs
|
||||
} else {
|
||||
let shifted_lhs = lhs >> (nbits - 1) >> 1;
|
||||
let lhs_is_reduced = lhs != 0;
|
||||
(shifted_lhs == rhs && lhs_is_reduced) || shifted_lhs > rhs
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ge(&self, rhs: &$Fixed<FracRhs>) -> bool {
|
||||
let lhs = self.to_bits();
|
||||
let rhs = rhs.to_bits();
|
||||
|
||||
// lhs_extra_frac and nbits are known exactly at compile time,
|
||||
// so with optimizations the branch is selected at compile time
|
||||
let lhs_extra_frac = FracLhs::to_i32().saturating_sub(FracRhs::to_i32());
|
||||
let nbits = $Inner::BITS as i32;
|
||||
if lhs_extra_frac <= -nbits {
|
||||
let shifted_rhs = rhs >> (nbits - 1) >> 1;
|
||||
let rhs_is_reduced = rhs != 0;
|
||||
!(lhs == shifted_rhs && rhs_is_reduced) && lhs >= shifted_rhs
|
||||
} else if lhs_extra_frac < 0 {
|
||||
let shifted_rhs = rhs >> -lhs_extra_frac;
|
||||
let rhs_is_reduced = rhs != (shifted_rhs << -lhs_extra_frac);
|
||||
!(lhs == shifted_rhs && rhs_is_reduced) && lhs >= shifted_rhs
|
||||
} else if lhs_extra_frac == 0 {
|
||||
lhs >= rhs
|
||||
} else if lhs_extra_frac < nbits {
|
||||
let shifted_lhs = lhs >> lhs_extra_frac;
|
||||
shifted_lhs >= rhs
|
||||
} else {
|
||||
let shifted_lhs = lhs >> (nbits - 1) >> 1;
|
||||
shifted_lhs >= rhs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: Unsigned> Eq for $Fixed<Frac> {}
|
||||
|
||||
impl<Frac: Unsigned> Ord for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn cmp(&self, rhs: &$Fixed<Frac>) -> Ordering {
|
||||
let lhs = self.to_bits();
|
||||
let rhs = rhs.to_bits();
|
||||
Ord::cmp(&lhs, &rhs)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
cmp! { FixedI8(i8) }
|
||||
cmp! { FixedI16(i16) }
|
||||
cmp! { FixedI32(i32) }
|
||||
cmp! { FixedI64(i64) }
|
||||
cmp! { FixedI128(i128) }
|
||||
cmp! { FixedU8(u8) }
|
||||
cmp! { FixedU16(u16) }
|
||||
cmp! { FixedU32(u32) }
|
||||
cmp! { FixedU64(u64) }
|
||||
cmp! { FixedU128(u128) }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn issue_57() {
|
||||
use crate::types::I80F48;
|
||||
let a: u64 = 66000;
|
||||
let b: u64 = 1000;
|
||||
assert!(I80F48::from(a) > b);
|
||||
}
|
||||
}
|
||||
Vendored
+606
@@ -0,0 +1,606 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
/*!
|
||||
Mathematical constants.
|
||||
|
||||
The constants have the maximum precision possible for a fixed-point
|
||||
number, and are rounded down at that precision.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::consts;
|
||||
use fixed::types::I4F28;
|
||||
let tau = I4F28::from_num(consts::TAU);
|
||||
println!("τ = 2π with eight binary places is {tau:.8b}");
|
||||
assert_eq!(format!("{tau:.8b}"), "110.01001000");
|
||||
println!("τ = 2π with eight decimal places is {tau:.8}");
|
||||
assert_eq!(format!("{tau:.8}"), "6.28318531");
|
||||
```
|
||||
*/
|
||||
|
||||
use crate::types::{U0F128, U1F127, U2F126, U3F125};
|
||||
|
||||
/*
|
||||
```rust
|
||||
use core::cmp::Ord;
|
||||
use rug::float::{Constant, Round};
|
||||
use rug::{Assign, Float};
|
||||
|
||||
fn decimal_string(val: &Float, num_digits: usize) -> String {
|
||||
let (sign, mut digits, exp) = val.to_sign_string_exp_round(10, Some(num_digits), Round::Down);
|
||||
if let Some(exp) = exp {
|
||||
if exp <= 0 {
|
||||
let one_minus_exp = usize::try_from(1 - exp).unwrap();
|
||||
digits.insert_str(0, &"0".repeat(one_minus_exp));
|
||||
digits.insert(1, '.');
|
||||
} else {
|
||||
let exp = usize::try_from(exp).unwrap();
|
||||
if exp >= num_digits {
|
||||
digits.push_str(&"0".repeat(exp - num_digits));
|
||||
} else {
|
||||
digits.insert(exp, '.');
|
||||
}
|
||||
}
|
||||
}
|
||||
if sign {
|
||||
digits.insert(0, '-');
|
||||
}
|
||||
digits
|
||||
}
|
||||
|
||||
fn hex_bits(val: &Float, frac_bits: i32) -> String {
|
||||
let val = val.clone() << frac_bits;
|
||||
let int = val.to_integer_round(Round::Down).unwrap().0;
|
||||
let mut s = format!("0x{int:016X}");
|
||||
for i in 0..7 {
|
||||
s.insert(6 + 5 * i, '_');
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn print(doc: &str, name: &str, val: Float) {
|
||||
println!("/// {} = {}…", doc, decimal_string(&val, 6));
|
||||
println!("// {} = {}...", name, decimal_string(&val, 40));
|
||||
let int_bits = val.get_exp().unwrap().max(0);
|
||||
let frac_bits = 128 - int_bits;
|
||||
print!("pub const {}: U{}F{} = U{1}F{2}", name, int_bits, frac_bits,);
|
||||
println!("::from_bits({});", hex_bits(&val, frac_bits));
|
||||
println!();
|
||||
}
|
||||
|
||||
fn float<T>(t: T) -> Float
|
||||
where
|
||||
Float: Assign<T>,
|
||||
{
|
||||
Float::with_val(1000, t)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
print("A turn, τ", "TAU", float(Constant::Pi) * 2);
|
||||
print("τ/2", "FRAC_TAU_2", float(Constant::Pi));
|
||||
print("τ/3", "FRAC_TAU_3", float(Constant::Pi) * 2 / 3);
|
||||
print("τ/4", "FRAC_TAU_4", float(Constant::Pi) / 2);
|
||||
print("τ/6", "FRAC_TAU_6", float(Constant::Pi) / 3);
|
||||
print("τ/8", "FRAC_TAU_8", float(Constant::Pi) / 4);
|
||||
print("τ/12", "FRAC_TAU_12", float(Constant::Pi) / 6);
|
||||
print("1/τ", "FRAC_1_TAU", 0.5 / float(Constant::Pi));
|
||||
print("2/τ", "FRAC_2_TAU", 1 / float(Constant::Pi));
|
||||
print("4/τ", "FRAC_4_TAU", 2 / float(Constant::Pi));
|
||||
print("Archimedes’ constant, π", "PI", float(Constant::Pi));
|
||||
print("π/2", "FRAC_PI_2", float(Constant::Pi) / 2);
|
||||
print("π/3", "FRAC_PI_3", float(Constant::Pi) / 3);
|
||||
print("π/4", "FRAC_PI_4", float(Constant::Pi) / 4);
|
||||
print("π/6", "FRAC_PI_6", float(Constant::Pi) / 6);
|
||||
print("π/8", "FRAC_PI_8", float(Constant::Pi) / 8);
|
||||
print("1/π", "FRAC_1_PI", 1 / float(Constant::Pi));
|
||||
print("2/π", "FRAC_2_PI", 2 / float(Constant::Pi));
|
||||
print("√π", "SQRT_PI", float(Constant::Pi).sqrt());
|
||||
print("√2π", "SQRT_2PI", (float(Constant::Pi) * 2u32).sqrt());
|
||||
print("1/√π", "FRAC_1_SQRT_PI", 1 / float(Constant::Pi).sqrt());
|
||||
print("1/√2π", "FRAC_1_SQRT_2PI", 1 / (float(Constant::Pi) * 2u32).sqrt());
|
||||
print("2/√π", "FRAC_2_SQRT_PI", 2 / float(Constant::Pi).sqrt());
|
||||
print("√2", "SQRT_2", float(2).sqrt());
|
||||
print("1/√2", "FRAC_1_SQRT_2", float(0.5).sqrt());
|
||||
print("√3", "SQRT_3", float(3).sqrt());
|
||||
print("1/√3", "FRAC_1_SQRT_3", float(3).recip().sqrt());
|
||||
print("Euler’s number, e", "E", float(1).exp());
|
||||
print("√e", "SQRT_E", float(0.5).exp());
|
||||
print("log<sub>2</sub> 10", "LOG2_10", float(10).log2());
|
||||
print("log<sub>2</sub> e", "LOG2_E", float(1).exp().log2());
|
||||
print("log<sub>10</sub> 2", "LOG10_2", float(2).log10());
|
||||
print("log<sub>10</sub> e", "LOG10_E", float(1).exp().log10());
|
||||
print("ln 2", "LN_2", float(2).ln());
|
||||
print("ln 10", "LN_10", float(10).ln());
|
||||
print("The golden ratio, φ", "PHI", float(1.25).sqrt() + 0.5);
|
||||
print("The golden ratio conjugate, Φ = 1/φ", "FRAC_1_PHI", float(1.25).sqrt() - 0.5);
|
||||
print("√φ", "SQRT_PHI", (float(1.25).sqrt() + 0.5f32).sqrt());
|
||||
print("The Euler-Mascheroni constant, γ", "GAMMA", float(Constant::Euler));
|
||||
print("Catalan’s constant", "CATALAN", float(Constant::Catalan));
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
/// A turn, τ = 6.28318…
|
||||
// TAU = 6.283185307179586476925286766559005768394...
|
||||
pub const TAU: U3F125 = U3F125::from_bits(0xC90F_DAA2_2168_C234_C4C6_628B_80DC_1CD1);
|
||||
|
||||
/// τ/2 = 3.14159…
|
||||
// FRAC_TAU_2 = 3.141592653589793238462643383279502884197...
|
||||
pub const FRAC_TAU_2: U2F126 = U2F126::from_bits(0xC90F_DAA2_2168_C234_C4C6_628B_80DC_1CD1);
|
||||
|
||||
/// τ/3 = 2.09439…
|
||||
// FRAC_TAU_3 = 2.094395102393195492308428922186335256131...
|
||||
pub const FRAC_TAU_3: U2F126 = U2F126::from_bits(0x860A_91C1_6B9B_2C23_2DD9_9707_AB3D_688B);
|
||||
|
||||
/// τ/4 = 1.57079…
|
||||
// FRAC_TAU_4 = 1.570796326794896619231321691639751442098...
|
||||
pub const FRAC_TAU_4: U1F127 = U1F127::from_bits(0xC90F_DAA2_2168_C234_C4C6_628B_80DC_1CD1);
|
||||
|
||||
/// τ/6 = 1.04719…
|
||||
// FRAC_TAU_6 = 1.047197551196597746154214461093167628065...
|
||||
pub const FRAC_TAU_6: U1F127 = U1F127::from_bits(0x860A_91C1_6B9B_2C23_2DD9_9707_AB3D_688B);
|
||||
|
||||
/// τ/8 = 0.785398…
|
||||
// FRAC_TAU_8 = 0.7853981633974483096156608458198757210492...
|
||||
pub const FRAC_TAU_8: U0F128 = U0F128::from_bits(0xC90F_DAA2_2168_C234_C4C6_628B_80DC_1CD1);
|
||||
|
||||
/// τ/12 = 0.523598…
|
||||
// FRAC_TAU_12 = 0.5235987755982988730771072305465838140328...
|
||||
pub const FRAC_TAU_12: U0F128 = U0F128::from_bits(0x860A_91C1_6B9B_2C23_2DD9_9707_AB3D_688B);
|
||||
|
||||
/// 1/τ = 0.159154…
|
||||
// FRAC_1_TAU = 0.1591549430918953357688837633725143620344...
|
||||
pub const FRAC_1_TAU: U0F128 = U0F128::from_bits(0x28BE_60DB_9391_054A_7F09_D5F4_7D4D_3770);
|
||||
|
||||
/// 2/τ = 0.318309…
|
||||
// FRAC_2_TAU = 0.3183098861837906715377675267450287240689...
|
||||
pub const FRAC_2_TAU: U0F128 = U0F128::from_bits(0x517C_C1B7_2722_0A94_FE13_ABE8_FA9A_6EE0);
|
||||
|
||||
/// 4/τ = 0.636619…
|
||||
// FRAC_4_TAU = 0.6366197723675813430755350534900574481378...
|
||||
pub const FRAC_4_TAU: U0F128 = U0F128::from_bits(0xA2F9_836E_4E44_1529_FC27_57D1_F534_DDC0);
|
||||
|
||||
/// Archimedes’ constant, π = 3.14159…
|
||||
// PI = 3.141592653589793238462643383279502884197...
|
||||
pub const PI: U2F126 = U2F126::from_bits(0xC90F_DAA2_2168_C234_C4C6_628B_80DC_1CD1);
|
||||
|
||||
/// π/2 = 1.57079…
|
||||
// FRAC_PI_2 = 1.570796326794896619231321691639751442098...
|
||||
pub const FRAC_PI_2: U1F127 = U1F127::from_bits(0xC90F_DAA2_2168_C234_C4C6_628B_80DC_1CD1);
|
||||
|
||||
/// π/3 = 1.04719…
|
||||
// FRAC_PI_3 = 1.047197551196597746154214461093167628065...
|
||||
pub const FRAC_PI_3: U1F127 = U1F127::from_bits(0x860A_91C1_6B9B_2C23_2DD9_9707_AB3D_688B);
|
||||
|
||||
/// π/4 = 0.785398…
|
||||
// FRAC_PI_4 = 0.7853981633974483096156608458198757210492...
|
||||
pub const FRAC_PI_4: U0F128 = U0F128::from_bits(0xC90F_DAA2_2168_C234_C4C6_628B_80DC_1CD1);
|
||||
|
||||
/// π/6 = 0.523598…
|
||||
// FRAC_PI_6 = 0.5235987755982988730771072305465838140328...
|
||||
pub const FRAC_PI_6: U0F128 = U0F128::from_bits(0x860A_91C1_6B9B_2C23_2DD9_9707_AB3D_688B);
|
||||
|
||||
/// π/8 = 0.392699…
|
||||
// FRAC_PI_8 = 0.3926990816987241548078304229099378605246...
|
||||
pub const FRAC_PI_8: U0F128 = U0F128::from_bits(0x6487_ED51_10B4_611A_6263_3145_C06E_0E68);
|
||||
|
||||
/// 1/π = 0.318309…
|
||||
// FRAC_1_PI = 0.3183098861837906715377675267450287240689...
|
||||
pub const FRAC_1_PI: U0F128 = U0F128::from_bits(0x517C_C1B7_2722_0A94_FE13_ABE8_FA9A_6EE0);
|
||||
|
||||
/// 2/π = 0.636619…
|
||||
// FRAC_2_PI = 0.6366197723675813430755350534900574481378...
|
||||
pub const FRAC_2_PI: U0F128 = U0F128::from_bits(0xA2F9_836E_4E44_1529_FC27_57D1_F534_DDC0);
|
||||
|
||||
/// √π = 1.77245…
|
||||
// SQRT_PI = 1.772453850905516027298167483341145182797...
|
||||
pub const SQRT_PI: U1F127 = U1F127::from_bits(0xE2DF_C48D_A77B_553C_E1D8_2906_AEDC_9C1F);
|
||||
|
||||
/// √2π = 2.50662…
|
||||
// SQRT_2PI = 2.506628274631000502415765284811045253006...
|
||||
pub const SQRT_2PI: U2F126 = U2F126::from_bits(0xA06C_98FF_B138_2CB2_BE52_0FD7_3916_7717);
|
||||
|
||||
/// 1/√π = 0.564189…
|
||||
// FRAC_1_SQRT_PI = 0.5641895835477562869480794515607725858440...
|
||||
pub const FRAC_1_SQRT_PI: U0F128 = U0F128::from_bits(0x906E_BA82_14DB_688D_71D4_8A7F_6BFE_C344);
|
||||
|
||||
/// 1/√2π = 0.398942…
|
||||
// FRAC_1_SQRT_2PI = 0.3989422804014326779399460599343818684758...
|
||||
pub const FRAC_1_SQRT_2PI: U0F128 = U0F128::from_bits(0x6621_14CF_50D9_4234_3F2C_F140_2EAE_38BF);
|
||||
|
||||
/// 2/√π = 1.12837…
|
||||
// FRAC_2_SQRT_PI = 1.128379167095512573896158903121545171688...
|
||||
pub const FRAC_2_SQRT_PI: U1F127 = U1F127::from_bits(0x906E_BA82_14DB_688D_71D4_8A7F_6BFE_C344);
|
||||
|
||||
/// √2 = 1.41421…
|
||||
// SQRT_2 = 1.414213562373095048801688724209698078569...
|
||||
pub const SQRT_2: U1F127 = U1F127::from_bits(0xB504_F333_F9DE_6484_597D_89B3_754A_BE9F);
|
||||
|
||||
/// 1/√2 = 0.707106…
|
||||
// FRAC_1_SQRT_2 = 0.7071067811865475244008443621048490392848...
|
||||
pub const FRAC_1_SQRT_2: U0F128 = U0F128::from_bits(0xB504_F333_F9DE_6484_597D_89B3_754A_BE9F);
|
||||
|
||||
/// √3 = 1.73205…
|
||||
// SQRT_3 = 1.732050807568877293527446341505872366942...
|
||||
pub const SQRT_3: U1F127 = U1F127::from_bits(0xDDB3_D742_C265_539D_92BA_16B8_3C5C_1DC4);
|
||||
|
||||
/// 1/√3 = 0.577350…
|
||||
// FRAC_1_SQRT_3 = 0.5773502691896257645091487805019574556476...
|
||||
pub const FRAC_1_SQRT_3: U0F128 = U0F128::from_bits(0x93CD_3A2C_8198_E269_0C7C_0F25_7D92_BE83);
|
||||
|
||||
/// Euler’s number, e = 2.71828…
|
||||
// E = 2.718281828459045235360287471352662497757...
|
||||
pub const E: U2F126 = U2F126::from_bits(0xADF8_5458_A2BB_4A9A_AFDC_5620_273D_3CF1);
|
||||
|
||||
/// √e = 1.64872…
|
||||
// SQRT_E = 1.648721270700128146848650787814163571653...
|
||||
pub const SQRT_E: U1F127 = U1F127::from_bits(0xD309_4C70_F034_DE4B_96FF_7D5B_6F99_FCD8);
|
||||
|
||||
/// log<sub>2</sub> 10 = 3.32192…
|
||||
// LOG2_10 = 3.321928094887362347870319429489390175864...
|
||||
pub const LOG2_10: U2F126 = U2F126::from_bits(0xD49A_784B_CD1B_8AFE_492B_F6FF_4DAF_DB4C);
|
||||
|
||||
/// log<sub>2</sub> e = 1.44269…
|
||||
// LOG2_E = 1.442695040888963407359924681001892137426...
|
||||
pub const LOG2_E: U1F127 = U1F127::from_bits(0xB8AA_3B29_5C17_F0BB_BE87_FED0_691D_3E88);
|
||||
|
||||
/// log<sub>10</sub> 2 = 0.301029…
|
||||
// LOG10_2 = 0.3010299956639811952137388947244930267681...
|
||||
pub const LOG10_2: U0F128 = U0F128::from_bits(0x4D10_4D42_7DE7_FBCC_47C4_ACD6_05BE_48BC);
|
||||
|
||||
/// log<sub>10</sub> e = 0.434294…
|
||||
// LOG10_E = 0.4342944819032518276511289189166050822943...
|
||||
pub const LOG10_E: U0F128 = U0F128::from_bits(0x6F2D_EC54_9B94_38CA_9AAD_D557_D699_EE19);
|
||||
|
||||
/// ln 2 = 0.693147…
|
||||
// LN_2 = 0.6931471805599453094172321214581765680755...
|
||||
pub const LN_2: U0F128 = U0F128::from_bits(0xB172_17F7_D1CF_79AB_C9E3_B398_03F2_F6AF);
|
||||
|
||||
/// ln 10 = 2.30258…
|
||||
// LN_10 = 2.302585092994045684017991454684364207601...
|
||||
pub const LN_10: U2F126 = U2F126::from_bits(0x935D_8DDD_AAA8_AC16_EA56_D62B_82D3_0A28);
|
||||
|
||||
/// The golden ratio, φ = 1.61803…
|
||||
// PHI = 1.618033988749894848204586834365638117720...
|
||||
pub const PHI: U1F127 = U1F127::from_bits(0xCF1B_BCDC_BFA5_3E0A_F9CE_6030_2E76_E41A);
|
||||
|
||||
/// The golden ratio conjugate, Φ = 1/φ = 0.618033…
|
||||
// FRAC_1_PHI = 0.6180339887498948482045868343656381177203...
|
||||
pub const FRAC_1_PHI: U0F128 = U0F128::from_bits(0x9E37_79B9_7F4A_7C15_F39C_C060_5CED_C834);
|
||||
|
||||
/// √φ = 1.27201…
|
||||
// SQRT_PHI = 1.272019649514068964252422461737491491715...
|
||||
pub const SQRT_PHI: U1F127 = U1F127::from_bits(0xA2D1_8A35_4422_AF49_2AA2_8089_0F62_6C86);
|
||||
|
||||
/// The Euler-Mascheroni constant, γ = 0.577215…
|
||||
// GAMMA = 0.5772156649015328606065120900824024310421...
|
||||
pub const GAMMA: U0F128 = U0F128::from_bits(0x93C4_67E3_7DB0_C7A4_D1BE_3F81_0152_CB56);
|
||||
|
||||
/// Catalan’s constant = 0.915965…
|
||||
// CATALAN = 0.9159655941772190150546035149323841107741...
|
||||
pub const CATALAN: U0F128 = U0F128::from_bits(0xEA7C_B89F_409A_E845_2158_22E3_7D32_D0C6);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::consts::*;
|
||||
use crate::traits::{Fixed, FromFixed};
|
||||
use core::{f32, f64};
|
||||
|
||||
#[test]
|
||||
fn cmp_f16() {
|
||||
use half;
|
||||
use half::f16 as half_f16;
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(TAU),
|
||||
half_f16::from_f32(f32::consts::PI * 2.0)
|
||||
);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_TAU_2), half_f16::PI);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(FRAC_TAU_3),
|
||||
half_f16::from_f32(f32::consts::FRAC_PI_3 * 2.0)
|
||||
);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_TAU_4), half_f16::FRAC_PI_2);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_TAU_6), half_f16::FRAC_PI_3);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_TAU_8), half_f16::FRAC_PI_4);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_TAU_12), half_f16::FRAC_PI_6);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(FRAC_1_TAU),
|
||||
half_f16::from_f32(f32::consts::FRAC_1_PI * 0.5)
|
||||
);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_2_TAU), half_f16::FRAC_1_PI);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_4_TAU), half_f16::FRAC_2_PI);
|
||||
assert_eq!(half_f16::from_fixed(PI), half_f16::PI);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_PI_2), half_f16::FRAC_PI_2);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_PI_3), half_f16::FRAC_PI_3);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_PI_4), half_f16::FRAC_PI_4);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_PI_6), half_f16::FRAC_PI_6);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_PI_8), half_f16::FRAC_PI_8);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_1_PI), half_f16::FRAC_1_PI);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_2_PI), half_f16::FRAC_2_PI);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(SQRT_PI),
|
||||
half_f16::from_f32(f32::consts::PI.sqrt())
|
||||
);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(FRAC_1_SQRT_PI),
|
||||
half_f16::from_f32(f32::consts::FRAC_2_SQRT_PI / 2.0)
|
||||
);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(FRAC_2_SQRT_PI),
|
||||
half_f16::FRAC_2_SQRT_PI
|
||||
);
|
||||
assert_eq!(half_f16::from_fixed(SQRT_2), half_f16::SQRT_2);
|
||||
assert_eq!(half_f16::from_fixed(FRAC_1_SQRT_2), half_f16::FRAC_1_SQRT_2);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(SQRT_3),
|
||||
half_f16::from_f32(3f32.sqrt())
|
||||
);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(FRAC_1_SQRT_3),
|
||||
half_f16::from_f32(3f32.powf(-0.5))
|
||||
);
|
||||
assert_eq!(half_f16::from_fixed(E), half_f16::E);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(SQRT_E),
|
||||
half_f16::from_f32(0.5f32.exp())
|
||||
);
|
||||
assert_eq!(half_f16::from_fixed(LOG2_10), half_f16::LOG2_10);
|
||||
assert_eq!(half_f16::from_fixed(LOG2_E), half_f16::LOG2_E);
|
||||
assert_eq!(half_f16::from_fixed(LOG10_2), half_f16::LOG10_2);
|
||||
assert_eq!(half_f16::from_fixed(LOG10_E), half_f16::LOG10_E);
|
||||
assert_eq!(half_f16::from_fixed(LN_2), half_f16::LN_2);
|
||||
assert_eq!(half_f16::from_fixed(LN_10), half_f16::LN_10);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(PHI),
|
||||
half_f16::from_f32(1.25f32.sqrt() + 0.5)
|
||||
);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(FRAC_1_PHI),
|
||||
half_f16::from_f32(1.25f32.sqrt() - 0.5)
|
||||
);
|
||||
assert_eq!(
|
||||
half_f16::from_fixed(SQRT_PHI),
|
||||
half_f16::from_f32((1.25f32.sqrt() + 0.5f32).sqrt())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_bf16() {
|
||||
use half;
|
||||
use half::bf16 as half_bf16;
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(TAU),
|
||||
half_bf16::from_f32(f32::consts::PI * 2.0)
|
||||
);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_TAU_2), half_bf16::PI);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(FRAC_TAU_3),
|
||||
half_bf16::from_f32(f32::consts::FRAC_PI_3 * 2.0)
|
||||
);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_TAU_4), half_bf16::FRAC_PI_2);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_TAU_6), half_bf16::FRAC_PI_3);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_TAU_8), half_bf16::FRAC_PI_4);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_TAU_12), half_bf16::FRAC_PI_6);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(FRAC_1_TAU),
|
||||
half_bf16::from_f32(f32::consts::FRAC_1_PI * 0.5)
|
||||
);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_2_TAU), half_bf16::FRAC_1_PI);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_4_TAU), half_bf16::FRAC_2_PI);
|
||||
assert_eq!(half_bf16::from_fixed(PI), half_bf16::PI);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_PI_2), half_bf16::FRAC_PI_2);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_PI_3), half_bf16::FRAC_PI_3);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_PI_4), half_bf16::FRAC_PI_4);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_PI_6), half_bf16::FRAC_PI_6);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_PI_8), half_bf16::FRAC_PI_8);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_1_PI), half_bf16::FRAC_1_PI);
|
||||
assert_eq!(half_bf16::from_fixed(FRAC_2_PI), half_bf16::FRAC_2_PI);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(SQRT_PI),
|
||||
half_bf16::from_f32(f32::consts::PI.sqrt())
|
||||
);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(FRAC_1_SQRT_PI),
|
||||
half_bf16::from_f32(f32::consts::FRAC_2_SQRT_PI / 2.0)
|
||||
);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(FRAC_2_SQRT_PI),
|
||||
half_bf16::FRAC_2_SQRT_PI
|
||||
);
|
||||
assert_eq!(half_bf16::from_fixed(SQRT_2), half_bf16::SQRT_2);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(FRAC_1_SQRT_2),
|
||||
half_bf16::FRAC_1_SQRT_2
|
||||
);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(SQRT_3),
|
||||
half_bf16::from_f32(3f32.sqrt())
|
||||
);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(FRAC_1_SQRT_3),
|
||||
half_bf16::from_f32(3f32.powf(-0.5))
|
||||
);
|
||||
assert_eq!(half_bf16::from_fixed(E), half_bf16::E);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(SQRT_E),
|
||||
half_bf16::from_f32(0.5f32.exp())
|
||||
);
|
||||
assert_eq!(half_bf16::from_fixed(LOG2_10), half_bf16::LOG2_10);
|
||||
assert_eq!(half_bf16::from_fixed(LOG2_E), half_bf16::LOG2_E);
|
||||
assert_eq!(half_bf16::from_fixed(LOG10_2), half_bf16::LOG10_2);
|
||||
assert_eq!(half_bf16::from_fixed(LOG10_E), half_bf16::LOG10_E);
|
||||
assert_eq!(half_bf16::from_fixed(LN_2), half_bf16::LN_2);
|
||||
assert_eq!(half_bf16::from_fixed(LN_10), half_bf16::LN_10);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(PHI),
|
||||
half_bf16::from_f32(1.25f32.sqrt() + 0.5)
|
||||
);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(FRAC_1_PHI),
|
||||
half_bf16::from_f32(1.25f32.sqrt() - 0.5)
|
||||
);
|
||||
assert_eq!(
|
||||
half_bf16::from_fixed(SQRT_PHI),
|
||||
half_bf16::from_f32((1.25f32.sqrt() + 0.5f32).sqrt())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_f32() {
|
||||
assert_eq!(f32::from_fixed(TAU), f32::consts::PI * 2.0);
|
||||
assert_eq!(f32::from_fixed(FRAC_TAU_2), f32::consts::PI);
|
||||
assert_eq!(f32::from_fixed(FRAC_TAU_3), f32::consts::FRAC_PI_3 * 2.0);
|
||||
assert_eq!(f32::from_fixed(FRAC_TAU_4), f32::consts::FRAC_PI_2);
|
||||
assert_eq!(f32::from_fixed(FRAC_TAU_6), f32::consts::FRAC_PI_3);
|
||||
assert_eq!(f32::from_fixed(FRAC_TAU_8), f32::consts::FRAC_PI_4);
|
||||
assert_eq!(f32::from_fixed(FRAC_TAU_12), f32::consts::FRAC_PI_6);
|
||||
assert_eq!(f32::from_fixed(FRAC_1_TAU), f32::consts::FRAC_1_PI * 0.5);
|
||||
assert_eq!(f32::from_fixed(FRAC_2_TAU), f32::consts::FRAC_1_PI);
|
||||
assert_eq!(f32::from_fixed(FRAC_4_TAU), f32::consts::FRAC_2_PI);
|
||||
assert_eq!(f32::from_fixed(PI), f32::consts::PI);
|
||||
assert_eq!(f32::from_fixed(FRAC_PI_2), f32::consts::FRAC_PI_2);
|
||||
assert_eq!(f32::from_fixed(FRAC_PI_3), f32::consts::FRAC_PI_3);
|
||||
assert_eq!(f32::from_fixed(FRAC_PI_4), f32::consts::FRAC_PI_4);
|
||||
assert_eq!(f32::from_fixed(FRAC_PI_6), f32::consts::FRAC_PI_6);
|
||||
assert_eq!(f32::from_fixed(FRAC_PI_8), f32::consts::FRAC_PI_8);
|
||||
assert_eq!(f32::from_fixed(FRAC_1_PI), f32::consts::FRAC_1_PI);
|
||||
assert_eq!(f32::from_fixed(FRAC_2_PI), f32::consts::FRAC_2_PI);
|
||||
assert_eq!(f32::from_fixed(SQRT_PI), f64::consts::PI.sqrt() as f32);
|
||||
assert_eq!(
|
||||
f32::from_fixed(FRAC_1_SQRT_PI),
|
||||
f32::consts::FRAC_2_SQRT_PI / 2.0
|
||||
);
|
||||
assert_eq!(f32::from_fixed(FRAC_2_SQRT_PI), f32::consts::FRAC_2_SQRT_PI);
|
||||
assert_eq!(f32::from_fixed(SQRT_2), f32::consts::SQRT_2);
|
||||
assert_eq!(f32::from_fixed(FRAC_1_SQRT_2), f32::consts::FRAC_1_SQRT_2);
|
||||
assert_eq!(f32::from_fixed(SQRT_3), 3f32.sqrt());
|
||||
assert_eq!(f32::from_fixed(FRAC_1_SQRT_3), 3f32.powf(-0.5));
|
||||
assert_eq!(f32::from_fixed(E), f32::consts::E);
|
||||
assert_eq!(f32::from_fixed(SQRT_E), 0.5f32.exp());
|
||||
assert_eq!(f32::from_fixed(LOG2_10), f32::consts::LOG2_10);
|
||||
assert_eq!(f32::from_fixed(LOG2_E), f32::consts::LOG2_E);
|
||||
assert_eq!(f32::from_fixed(LOG10_2), f32::consts::LOG10_2);
|
||||
assert_eq!(f32::from_fixed(LOG10_E), f32::consts::LOG10_E);
|
||||
assert_eq!(f32::from_fixed(LN_2), f32::consts::LN_2);
|
||||
assert_eq!(f32::from_fixed(LN_10), f32::consts::LN_10);
|
||||
assert_eq!(f32::from_fixed(PHI), 1.25f32.sqrt() + 0.5);
|
||||
assert_eq!(f32::from_fixed(FRAC_1_PHI), (1.25f64.sqrt() - 0.5) as f32);
|
||||
assert_eq!(
|
||||
f32::from_fixed(SQRT_PHI),
|
||||
(1.25f64.sqrt() + 0.5f64).sqrt() as f32
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_f64() {
|
||||
assert_eq!(f64::from_fixed(TAU), f64::consts::PI * 2.0);
|
||||
assert_eq!(f64::from_fixed(FRAC_TAU_2), f64::consts::PI);
|
||||
assert_eq!(f64::from_fixed(FRAC_TAU_3), f64::consts::FRAC_PI_3 * 2.0);
|
||||
assert_eq!(f64::from_fixed(FRAC_TAU_4), f64::consts::FRAC_PI_2);
|
||||
assert_eq!(f64::from_fixed(FRAC_TAU_6), f64::consts::FRAC_PI_3);
|
||||
assert_eq!(f64::from_fixed(FRAC_TAU_8), f64::consts::FRAC_PI_4);
|
||||
assert_eq!(f64::from_fixed(FRAC_TAU_12), f64::consts::FRAC_PI_6);
|
||||
assert_eq!(f64::from_fixed(FRAC_1_TAU), f64::consts::FRAC_1_PI * 0.5);
|
||||
assert_eq!(f64::from_fixed(FRAC_2_TAU), f64::consts::FRAC_1_PI);
|
||||
assert_eq!(f64::from_fixed(FRAC_4_TAU), f64::consts::FRAC_2_PI);
|
||||
assert_eq!(f64::from_fixed(PI), f64::consts::PI);
|
||||
assert_eq!(f64::from_fixed(FRAC_PI_2), f64::consts::FRAC_PI_2);
|
||||
assert_eq!(f64::from_fixed(FRAC_PI_3), f64::consts::FRAC_PI_3);
|
||||
assert_eq!(f64::from_fixed(FRAC_PI_4), f64::consts::FRAC_PI_4);
|
||||
assert_eq!(f64::from_fixed(FRAC_PI_6), f64::consts::FRAC_PI_6);
|
||||
assert_eq!(f64::from_fixed(FRAC_PI_8), f64::consts::FRAC_PI_8);
|
||||
assert_eq!(f64::from_fixed(FRAC_1_PI), f64::consts::FRAC_1_PI);
|
||||
assert_eq!(f64::from_fixed(FRAC_2_PI), f64::consts::FRAC_2_PI);
|
||||
// Since 1 < SQRT_PI < 2, we use EPSILON.
|
||||
assert!((f64::from_fixed(SQRT_PI) - f64::consts::PI.sqrt()).abs() <= f64::EPSILON);
|
||||
assert_eq!(
|
||||
f64::from_fixed(FRAC_1_SQRT_PI),
|
||||
f64::consts::FRAC_2_SQRT_PI / 2.0
|
||||
);
|
||||
assert_eq!(f64::from_fixed(FRAC_2_SQRT_PI), f64::consts::FRAC_2_SQRT_PI);
|
||||
assert_eq!(f64::from_fixed(SQRT_2), f64::consts::SQRT_2);
|
||||
assert_eq!(f64::from_fixed(FRAC_1_SQRT_2), f64::consts::FRAC_1_SQRT_2);
|
||||
assert_eq!(f64::from_fixed(SQRT_3), 3f64.sqrt());
|
||||
assert_eq!(f64::from_fixed(FRAC_1_SQRT_3), 3f64.powf(-0.5));
|
||||
assert_eq!(f64::from_fixed(E), f64::consts::E);
|
||||
assert_eq!(f64::from_fixed(SQRT_E), 0.5f64.exp());
|
||||
assert_eq!(f64::from_fixed(LOG2_10), f64::consts::LOG2_10);
|
||||
assert_eq!(f64::from_fixed(LOG2_E), f64::consts::LOG2_E);
|
||||
assert_eq!(f64::from_fixed(LOG10_2), f64::consts::LOG10_2);
|
||||
assert_eq!(f64::from_fixed(LOG10_E), f64::consts::LOG10_E);
|
||||
assert_eq!(f64::from_fixed(LN_2), f64::consts::LN_2);
|
||||
assert_eq!(f64::from_fixed(LN_10), f64::consts::LN_10);
|
||||
assert_eq!(f64::from_fixed(PHI), 1.25f64.sqrt() + 0.5);
|
||||
// Since 0.5 < FRAC_1_PHI < 1, we use EPSILON / 2.
|
||||
assert!((f64::from_fixed(FRAC_1_PHI) - (1.25f64.sqrt() - 0.5)).abs() <= f64::EPSILON / 2.0);
|
||||
// Since 1 < SQRT_PHI < 2, we use EPSILON.
|
||||
assert!(
|
||||
(f64::from_fixed(SQRT_PHI) - (1.25f64.sqrt() + 0.5f64).sqrt()).abs() <= f64::EPSILON
|
||||
);
|
||||
}
|
||||
|
||||
fn compare_parse<F: Fixed>(f: F, s: &str)
|
||||
where
|
||||
F::Bits: TryFrom<u8>,
|
||||
{
|
||||
let sf = F::from_str(s).unwrap();
|
||||
let f_plus = f + F::from_bits(F::Bits::try_from(1).ok().unwrap());
|
||||
assert!(f <= sf && sf <= f_plus);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmp_parse() {
|
||||
compare_parse(TAU, "6.283185307179586476925286766559005768394");
|
||||
compare_parse(FRAC_TAU_2, "3.141592653589793238462643383279502884197");
|
||||
compare_parse(FRAC_TAU_3, "2.094395102393195492308428922186335256131");
|
||||
compare_parse(FRAC_TAU_4, "1.570796326794896619231321691639751442098");
|
||||
compare_parse(FRAC_TAU_6, "1.047197551196597746154214461093167628065");
|
||||
compare_parse(FRAC_TAU_8, "0.7853981633974483096156608458198757210492");
|
||||
compare_parse(FRAC_TAU_12, "0.5235987755982988730771072305465838140328");
|
||||
compare_parse(FRAC_1_TAU, "0.1591549430918953357688837633725143620344");
|
||||
compare_parse(FRAC_2_TAU, "0.3183098861837906715377675267450287240689");
|
||||
compare_parse(FRAC_4_TAU, "0.6366197723675813430755350534900574481378");
|
||||
compare_parse(PI, "3.141592653589793238462643383279502884197");
|
||||
compare_parse(FRAC_PI_2, "1.570796326794896619231321691639751442098");
|
||||
compare_parse(FRAC_PI_3, "1.047197551196597746154214461093167628065");
|
||||
compare_parse(FRAC_PI_4, "0.7853981633974483096156608458198757210492");
|
||||
compare_parse(FRAC_PI_6, "0.5235987755982988730771072305465838140328");
|
||||
compare_parse(FRAC_PI_8, "0.3926990816987241548078304229099378605246");
|
||||
compare_parse(FRAC_1_PI, "0.3183098861837906715377675267450287240689");
|
||||
compare_parse(FRAC_2_PI, "0.6366197723675813430755350534900574481378");
|
||||
compare_parse(SQRT_PI, "1.772453850905516027298167483341145182797");
|
||||
compare_parse(FRAC_1_SQRT_PI, "0.5641895835477562869480794515607725858440");
|
||||
compare_parse(FRAC_2_SQRT_PI, "1.128379167095512573896158903121545171688");
|
||||
compare_parse(SQRT_2, "1.414213562373095048801688724209698078569");
|
||||
compare_parse(FRAC_1_SQRT_2, "0.7071067811865475244008443621048490392848");
|
||||
compare_parse(SQRT_3, "1.732050807568877293527446341505872366942");
|
||||
compare_parse(FRAC_1_SQRT_3, "0.5773502691896257645091487805019574556476");
|
||||
compare_parse(E, "2.718281828459045235360287471352662497757");
|
||||
compare_parse(SQRT_E, "1.648721270700128146848650787814163571653");
|
||||
compare_parse(LOG2_10, "3.321928094887362347870319429489390175864");
|
||||
compare_parse(LOG2_E, "1.442695040888963407359924681001892137426");
|
||||
compare_parse(LOG10_2, "0.3010299956639811952137388947244930267681");
|
||||
compare_parse(LOG10_E, "0.4342944819032518276511289189166050822943");
|
||||
compare_parse(LN_2, "0.6931471805599453094172321214581765680755");
|
||||
compare_parse(LN_10, "2.302585092994045684017991454684364207601");
|
||||
compare_parse(PHI, "1.618033988749894848204586834365638117720");
|
||||
compare_parse(FRAC_1_PHI, "0.6180339887498948482045868343656381177203");
|
||||
compare_parse(SQRT_PHI, "1.272019649514068964252422461737491491715");
|
||||
compare_parse(GAMMA, "0.5772156649015328606065120900824024310421");
|
||||
compare_parse(CATALAN, "0.9159655941772190150546035149323841107741");
|
||||
}
|
||||
}
|
||||
Vendored
+1924
File diff suppressed because it is too large.
Load diff
Vendored
+134
@@ -0,0 +1,134 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use core::cell::Cell;
|
||||
use core::fmt::{Debug, Formatter, Result as FmtResult, Write};
|
||||
#[cfg(target_has_atomic = "32")]
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
// This is an ugly hack to check whether a `Formatter` has `debug_lower_hex` or
|
||||
// `debug_upper_hex`.
|
||||
//
|
||||
// We do a dummy write with format string "{:x?}" to get `debug_lower_hex`, and
|
||||
// a dummy write with format string "{:X?}" to get `debug_upper_hex`. Each time,
|
||||
// we get the flags using the deprecated `Formatter::flags`.
|
||||
//
|
||||
// If AtomicU32 is supported, we cache the flags.
|
||||
|
||||
#[cfg(target_has_atomic = "32")]
|
||||
static LOWER_FLAGS: AtomicU32 = AtomicU32::new(0);
|
||||
#[cfg(target_has_atomic = "32")]
|
||||
static UPPER_FLAGS: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
fn get_flags(f: &Formatter) -> u32 {
|
||||
#[allow(deprecated)]
|
||||
f.flags()
|
||||
}
|
||||
|
||||
struct StoreFlags(Cell<u32>);
|
||||
|
||||
impl Debug for StoreFlags {
|
||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||
self.0.set(get_flags(f));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Discard;
|
||||
|
||||
impl Write for Discard {
|
||||
fn write_str(&mut self, _s: &str) -> FmtResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub enum IsDebugHex {
|
||||
No,
|
||||
Lower,
|
||||
Upper,
|
||||
}
|
||||
|
||||
pub fn is_debug_hex(f: &Formatter) -> IsDebugHex {
|
||||
let flags = get_flags(f);
|
||||
// avoid doing unnecessary work if flags is zero
|
||||
if flags == 0 {
|
||||
return IsDebugHex::No;
|
||||
}
|
||||
|
||||
let (lower_mask, upper_mask) = get_flag_masks();
|
||||
|
||||
if flags & lower_mask != 0 {
|
||||
IsDebugHex::Lower
|
||||
} else if flags & upper_mask != 0 {
|
||||
IsDebugHex::Upper
|
||||
} else {
|
||||
IsDebugHex::No
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_has_atomic = "32")]
|
||||
fn load_cache() -> Option<(u32, u32)> {
|
||||
let cached_lower = LOWER_FLAGS.load(Ordering::Relaxed);
|
||||
let cached_upper = UPPER_FLAGS.load(Ordering::Relaxed);
|
||||
|
||||
if cached_lower == u32::MAX || cached_upper == u32::MAX {
|
||||
// error was detected, so no need to repeat the error generation
|
||||
return Some((0, 0));
|
||||
}
|
||||
if cached_lower == 0 || cached_upper == 0 {
|
||||
return None;
|
||||
}
|
||||
Some((cached_lower, cached_upper))
|
||||
}
|
||||
|
||||
#[cfg(target_has_atomic = "32")]
|
||||
fn store_cache(mut lower: u32, mut upper: u32) {
|
||||
if lower == 0 || upper == 0 {
|
||||
lower = u32::MAX;
|
||||
upper = u32::MAX;
|
||||
}
|
||||
LOWER_FLAGS.store(lower, Ordering::Relaxed);
|
||||
UPPER_FLAGS.store(upper, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[cfg(not(target_has_atomic = "32"))]
|
||||
fn load_cache() -> Option<(u32, u32)> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(target_has_atomic = "32"))]
|
||||
fn store_cache(_lower: u32, _upper: u32) {}
|
||||
|
||||
fn get_flag_masks() -> (u32, u32) {
|
||||
if let Some(cached) = load_cache() {
|
||||
return cached;
|
||||
}
|
||||
|
||||
let store_flags = StoreFlags(Cell::new(0));
|
||||
if write!(Discard, "{store_flags:x?}").is_err() {
|
||||
store_cache(u32::MAX, u32::MAX);
|
||||
return (0, 0);
|
||||
}
|
||||
let lower_flags = store_flags.0.get();
|
||||
if write!(Discard, "{store_flags:X?}").is_err() {
|
||||
store_cache(u32::MAX, u32::MAX);
|
||||
return (0, 0);
|
||||
}
|
||||
let upper_flags = store_flags.0.get();
|
||||
let lower_mask = lower_flags & !upper_flags;
|
||||
let upper_mask = upper_flags & !lower_flags;
|
||||
store_cache(lower_mask, upper_mask);
|
||||
(lower_mask, upper_mask)
|
||||
}
|
||||
Vendored
+1193
File diff suppressed because it is too large.
Load diff
Vendored
+1265
File diff suppressed because it is too large.
Load diff
Vendored
+232
@@ -0,0 +1,232 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
macro_rules! make_helper {
|
||||
($Float:ident($Bits:ty, $IBits:ident, $prec:expr) $(; use $path:path)?) => {
|
||||
#[allow(non_snake_case)]
|
||||
pub mod $Float {
|
||||
use crate::helpers::{FloatKind, ToFixedHelper, ToFloatHelper, Widest};
|
||||
use crate::int_helper;
|
||||
use core::cmp::Ordering;
|
||||
$(use $path as $Float;)?
|
||||
|
||||
const PREC: u32 = $prec;
|
||||
const EXP_BIAS: i32 = (1 << (<$Bits>::BITS - PREC - 1)) - 1;
|
||||
const EXP_MIN: i32 = 1 - EXP_BIAS;
|
||||
const EXP_MAX: i32 = EXP_BIAS;
|
||||
pub const SIGN_MASK: $Bits = 1 << (<$Bits>::BITS - 1);
|
||||
pub const EXP_MASK: $Bits = !(SIGN_MASK | MANT_MASK);
|
||||
const MANT_MASK: $Bits = (1 << (PREC - 1)) - 1;
|
||||
|
||||
// zero is NOT negative, that is zero is represented as
|
||||
// Kind::Finite { neg: false, abs: 0, frac_bits: 0 },
|
||||
pub enum Kind {
|
||||
NaN,
|
||||
Infinite { neg: bool },
|
||||
Finite { neg: bool, abs: $Bits, frac_bits: i32 },
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn kind(val: $Float) -> Kind {
|
||||
let (neg, mut exp, mut mantissa) = parts(val);
|
||||
if exp > EXP_MAX {
|
||||
return if mantissa == 0 {
|
||||
Kind::Infinite { neg }
|
||||
} else {
|
||||
Kind::NaN
|
||||
};
|
||||
}
|
||||
// if not subnormal, add implicit bit
|
||||
if exp >= EXP_MIN {
|
||||
mantissa |= 1 << (PREC - 1);
|
||||
} else {
|
||||
exp = EXP_MIN;
|
||||
}
|
||||
if mantissa == 0 {
|
||||
return Kind::Finite {
|
||||
neg: false,
|
||||
abs: 0,
|
||||
frac_bits: 0,
|
||||
};
|
||||
}
|
||||
Kind::Finite {
|
||||
neg,
|
||||
abs: mantissa,
|
||||
frac_bits: PREC as i32 - 1 - exp,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parts(val: $Float) -> (bool, i32, $Bits) {
|
||||
let bits = val.to_bits();
|
||||
let neg = bits & SIGN_MASK != 0;
|
||||
let biased_exp = (bits & EXP_MASK) >> (PREC - 1);
|
||||
let exp = biased_exp as i32 - EXP_BIAS;
|
||||
let mant = bits & MANT_MASK;
|
||||
|
||||
(neg, exp, mant)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_to_float_helper(
|
||||
val: ToFloatHelper,
|
||||
frac_bits: u32,
|
||||
int_bits: u32,
|
||||
) -> $Float {
|
||||
let fix_bits = frac_bits + int_bits;
|
||||
|
||||
let bits_sign = if val.neg { SIGN_MASK } else { 0 };
|
||||
|
||||
let extra_zeros = 128 - fix_bits;
|
||||
let leading_zeros = val.abs.leading_zeros() - extra_zeros;
|
||||
let signif_bits = fix_bits - leading_zeros;
|
||||
if signif_bits == 0 {
|
||||
return $Float::from_bits(bits_sign);
|
||||
}
|
||||
// remove leading zeros and implicit one
|
||||
let mut mantissa = val.abs << leading_zeros << 1;
|
||||
let exponent = int_bits as i32 - 1 - leading_zeros as i32;
|
||||
let biased_exponent = if exponent > EXP_MAX {
|
||||
return $Float::from_bits(EXP_MASK | bits_sign);
|
||||
} else if exponent < EXP_MIN {
|
||||
let lost_prec = EXP_MIN - exponent;
|
||||
if lost_prec as u32 >= (int_bits + frac_bits) {
|
||||
mantissa = 0;
|
||||
} else {
|
||||
// reinsert implicit one
|
||||
mantissa = (mantissa >> 1) | !(!0 >> 1);
|
||||
mantissa >>= lost_prec - 1;
|
||||
}
|
||||
0
|
||||
} else {
|
||||
(exponent + EXP_MAX) as $Bits
|
||||
};
|
||||
// check for rounding
|
||||
let round_up = (fix_bits >= PREC) && {
|
||||
let shift = PREC - 1;
|
||||
let mid_bit = !(!0 >> 1) >> (shift + extra_zeros);
|
||||
let lower_bits = mid_bit - 1;
|
||||
if mantissa & mid_bit == 0 {
|
||||
false
|
||||
} else if mantissa & lower_bits != 0 {
|
||||
true
|
||||
} else {
|
||||
// round to even
|
||||
mantissa & (mid_bit << 1) != 0
|
||||
}
|
||||
};
|
||||
let bits_exp = biased_exponent << (PREC - 1);
|
||||
let bits_mantissa = (if fix_bits >= PREC - 1 {
|
||||
(mantissa >> (fix_bits - (PREC - 1))) as $Bits
|
||||
} else {
|
||||
(mantissa as $Bits) << (PREC - 1 - fix_bits)
|
||||
}) & !(!0 << (PREC - 1));
|
||||
let mut bits_exp_mantissa = bits_exp | bits_mantissa;
|
||||
if round_up {
|
||||
bits_exp_mantissa += 1;
|
||||
}
|
||||
$Float::from_bits(bits_sign | bits_exp_mantissa)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_float_kind(val: $Float, dst_frac_bits: u32, dst_int_bits: u32) -> FloatKind {
|
||||
let prec = PREC as i32;
|
||||
|
||||
let (neg, mut exp, mut mantissa) = parts(val);
|
||||
if exp > EXP_MAX {
|
||||
return if mantissa == 0 {
|
||||
FloatKind::Infinite { neg }
|
||||
} else {
|
||||
FloatKind::NaN
|
||||
};
|
||||
}
|
||||
// if not subnormal add implicit bit, otherwise update exp
|
||||
if exp >= EXP_MIN {
|
||||
mantissa |= 1 << (prec - 1);
|
||||
} else {
|
||||
exp = EXP_MIN;
|
||||
}
|
||||
if mantissa == 0 {
|
||||
let conv = ToFixedHelper {
|
||||
bits: Widest::Unsigned(0),
|
||||
dir: Ordering::Equal,
|
||||
overflow: false,
|
||||
};
|
||||
return FloatKind::Finite { neg, conv };
|
||||
}
|
||||
|
||||
let mut src_frac_bits = prec - 1 - exp;
|
||||
let need_to_shr = src_frac_bits - dst_frac_bits as i32;
|
||||
if need_to_shr > prec {
|
||||
let dir = if neg {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
};
|
||||
let conv = ToFixedHelper {
|
||||
bits: Widest::Unsigned(0),
|
||||
dir,
|
||||
overflow: false,
|
||||
};
|
||||
return FloatKind::Finite { neg, conv };
|
||||
}
|
||||
let mut dir = Ordering::Equal;
|
||||
if need_to_shr > 0 {
|
||||
let removed_bits = mantissa & !(!0 << need_to_shr);
|
||||
let will_be_lsb = 1 << need_to_shr;
|
||||
let tie = will_be_lsb >> 1;
|
||||
if removed_bits == 0 {
|
||||
// removed nothing
|
||||
} else if removed_bits < tie {
|
||||
dir = Ordering::Less;
|
||||
} else if removed_bits > tie || mantissa & will_be_lsb != 0 {
|
||||
mantissa += will_be_lsb;
|
||||
dir = Ordering::Greater;
|
||||
} else {
|
||||
dir = Ordering::Less;
|
||||
};
|
||||
mantissa >>= need_to_shr;
|
||||
src_frac_bits -= need_to_shr;
|
||||
}
|
||||
let mut mantissa = mantissa as $IBits;
|
||||
if neg {
|
||||
mantissa = -mantissa;
|
||||
dir = dir.reverse();
|
||||
}
|
||||
let mut conv = int_helper::$IBits::to_fixed_helper(
|
||||
mantissa,
|
||||
src_frac_bits,
|
||||
dst_frac_bits,
|
||||
dst_int_bits,
|
||||
);
|
||||
conv.dir = dir;
|
||||
FloatKind::Finite { neg, conv }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly-float")]
|
||||
make_helper! { f16(u16, i16, 11) }
|
||||
make_helper! { half_f16(u16, i16, 11); use half::f16 }
|
||||
make_helper! { half_bf16(u16, i16, 8); use half::bf16 }
|
||||
make_helper! { f32(u32, i32, 24) }
|
||||
make_helper! { f64(u64, i64, 53) }
|
||||
#[cfg(feature = "nightly-float")]
|
||||
make_helper! { f128(u128, i128, 113) }
|
||||
make_helper! { F128(u128, i128, 113); use crate::F128 }
|
||||
make_helper! { F128Bits(u128, i128, 113); use crate::F128Bits }
|
||||
Vendored
+2821
File diff suppressed because it is too large.
Load diff
Vendored
+159
@@ -0,0 +1,159 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::int_helper;
|
||||
use crate::types::extra::{LeEqU8, LeEqU16, LeEqU32, LeEqU64, LeEqU128};
|
||||
use crate::{
|
||||
FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
|
||||
FixedU128,
|
||||
};
|
||||
use core::cmp::Ordering;
|
||||
|
||||
// Unsigned can have 0 ≤ x < 2^128, that is its msb can be 0 or 1.
|
||||
// Negative can have -2^127 ≤ x < 0, that is its msb must be 1.
|
||||
pub enum Widest {
|
||||
Unsigned(u128),
|
||||
Negative(i128),
|
||||
}
|
||||
|
||||
pub struct ToFixedHelper {
|
||||
pub(crate) bits: Widest,
|
||||
pub(crate) dir: Ordering,
|
||||
pub(crate) overflow: bool,
|
||||
}
|
||||
|
||||
pub struct ToFloatHelper {
|
||||
pub(crate) neg: bool,
|
||||
pub(crate) abs: u128,
|
||||
}
|
||||
|
||||
pub struct FromFloatHelper {
|
||||
pub(crate) kind: FloatKind,
|
||||
}
|
||||
pub enum FloatKind {
|
||||
NaN,
|
||||
Infinite { neg: bool },
|
||||
Finite { neg: bool, conv: ToFixedHelper },
|
||||
}
|
||||
|
||||
pub struct Private;
|
||||
|
||||
pub trait Sealed: Copy {
|
||||
fn to_fixed_helper(self, _: Private, dst_frac_nbits: u32, dst_int_nbits: u32) -> ToFixedHelper;
|
||||
fn to_float_helper(self, _: Private) -> ToFloatHelper;
|
||||
fn saturating_from_float_helper(_: Private, src: FromFloatHelper) -> Self;
|
||||
fn overflowing_from_float_helper(_: Private, src: FromFloatHelper) -> (Self, bool);
|
||||
}
|
||||
macro_rules! impl_sealed {
|
||||
($Fixed:ident($LeEqU:ident, $Signedness:ident, $Inner:ident)) => {
|
||||
impl<Frac: $LeEqU> Sealed for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn to_fixed_helper(
|
||||
self,
|
||||
_: Private,
|
||||
dst_frac_nbits: u32,
|
||||
dst_int_nbits: u32,
|
||||
) -> ToFixedHelper {
|
||||
int_helper::$Inner::to_fixed_helper(
|
||||
self.to_bits(),
|
||||
Self::FRAC_NBITS as i32,
|
||||
dst_frac_nbits,
|
||||
dst_int_nbits,
|
||||
)
|
||||
}
|
||||
#[inline]
|
||||
fn to_float_helper(self, _: Private) -> ToFloatHelper {
|
||||
let (neg, abs) = int_helper::$Inner::neg_abs(self.to_bits());
|
||||
let abs = abs.into();
|
||||
ToFloatHelper { neg, abs }
|
||||
}
|
||||
#[inline]
|
||||
fn saturating_from_float_helper(_: Private, src: FromFloatHelper) -> Self {
|
||||
let neg = match src.kind {
|
||||
FloatKind::NaN => panic!("NaN"),
|
||||
FloatKind::Infinite { neg } => neg,
|
||||
FloatKind::Finite { neg, .. } => neg,
|
||||
};
|
||||
let saturated = if neg { Self::MIN } else { Self::MAX };
|
||||
let FloatKind::Finite { conv, .. } = src.kind else {
|
||||
return saturated;
|
||||
};
|
||||
if conv.overflow {
|
||||
return saturated;
|
||||
}
|
||||
let bits = if_signed_unsigned!(
|
||||
$Signedness,
|
||||
match conv.bits {
|
||||
Widest::Unsigned(bits) => {
|
||||
let bits = bits as _;
|
||||
if bits < 0 {
|
||||
return Self::MAX;
|
||||
}
|
||||
bits
|
||||
}
|
||||
Widest::Negative(bits) => bits as _,
|
||||
},
|
||||
match conv.bits {
|
||||
Widest::Unsigned(bits) => bits as _,
|
||||
Widest::Negative(_) => return Self::MIN,
|
||||
},
|
||||
);
|
||||
Self::from_bits(bits)
|
||||
}
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn overflowing_from_float_helper(_: Private, src: FromFloatHelper) -> (Self, bool) {
|
||||
let conv = match src.kind {
|
||||
FloatKind::NaN => panic!("NaN"),
|
||||
FloatKind::Infinite { .. } => panic!("infinite"),
|
||||
FloatKind::Finite { conv, .. } => conv,
|
||||
};
|
||||
let mut new_overflow = false;
|
||||
let bits = if_signed_unsigned!(
|
||||
$Signedness,
|
||||
match conv.bits {
|
||||
Widest::Unsigned(bits) => {
|
||||
let bits = bits as _;
|
||||
if bits < 0 {
|
||||
new_overflow = true;
|
||||
}
|
||||
bits
|
||||
}
|
||||
Widest::Negative(bits) => bits as _,
|
||||
},
|
||||
match conv.bits {
|
||||
Widest::Unsigned(bits) => bits as _,
|
||||
Widest::Negative(bits) => {
|
||||
new_overflow = true;
|
||||
bits as _
|
||||
}
|
||||
},
|
||||
);
|
||||
(Self::from_bits(bits), conv.overflow || new_overflow)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_sealed! { FixedI8(LeEqU8, Signed, i8) }
|
||||
impl_sealed! { FixedI16(LeEqU16, Signed, i16) }
|
||||
impl_sealed! { FixedI32(LeEqU32, Signed, i32) }
|
||||
impl_sealed! { FixedI64(LeEqU64, Signed, i64) }
|
||||
impl_sealed! { FixedI128(LeEqU128, Signed, i128) }
|
||||
impl_sealed! { FixedU8(LeEqU8, Unsigned, u8) }
|
||||
impl_sealed! { FixedU16(LeEqU16, Unsigned, u16) }
|
||||
impl_sealed! { FixedU32(LeEqU32, Unsigned, u32) }
|
||||
impl_sealed! { FixedU64(LeEqU64, Unsigned, u64) }
|
||||
impl_sealed! { FixedU128(LeEqU128, Unsigned, u128) }
|
||||
Vendored
+246
@@ -0,0 +1,246 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::int256;
|
||||
use crate::int256::U256;
|
||||
use core::num::NonZero;
|
||||
|
||||
// The square root method used is based on code by Martin Guy @UKC, June 1985.
|
||||
// His method of square root by abacus method is from a book on programming
|
||||
// abaci by Mr C. Woo.
|
||||
// http://medialab.freaknet.org/martin/src/sqrt/
|
||||
|
||||
macro_rules! impl_hypot {
|
||||
($Single:ident, $Double:ident $(, $Half:ident)?) => {
|
||||
pub const fn $Single(a: $Single, b: $Single) -> ($Single, bool) {
|
||||
$(
|
||||
if a <= ($Half::MAX as $Single) && b <= ($Half::MAX as $Single) {
|
||||
let val = match $Half(a as $Half, b as $Half) {
|
||||
(val, false) => val as $Single,
|
||||
(val, true) => (val as $Single) + (1 << $Half::BITS),
|
||||
};
|
||||
return (val, false);
|
||||
}
|
||||
)?
|
||||
|
||||
let aa = (a as $Double) * (a as $Double);
|
||||
let bb = (b as $Double) * (b as $Double);
|
||||
let (sum, overflow) = aa.overflowing_add(bb);
|
||||
|
||||
let mut x = sum;
|
||||
let mut y;
|
||||
let mut bit;
|
||||
if overflow {
|
||||
// Perform initialization and two iterations assuming word wider than n bits.
|
||||
// Initialization:
|
||||
// Set y = 0
|
||||
// Set bit = 1 << n
|
||||
// First iteration:
|
||||
// Since x >= y + bit (because the overflow is equivalent to 1 << n on x):
|
||||
// x -= y + bit (removing the effect of the overflow on x)
|
||||
// y = (y >> 1) + bit (y becomes 1 << n)
|
||||
// bit >>= 2 (bit becomes 1 << (n - 2))
|
||||
// Second iteration:
|
||||
// Now x < y (because y is 1 << n):
|
||||
// y >>= 1 (y becomes 1 << (n - 1))
|
||||
// bit >>= 2 (bit becomes 1 << (n - 4))
|
||||
// In effect, we can set y = 1 << (n - 1), and bit = 1 << (n - 4)
|
||||
y = 1 << ($Double::BITS - 1);
|
||||
bit = 1 << ($Double::BITS - 4);
|
||||
} else {
|
||||
let sum_lz = match NonZero::<$Double>::new(sum) {
|
||||
None => return (0, false),
|
||||
Some(s) => s.leading_zeros(),
|
||||
};
|
||||
y = 0;
|
||||
bit = 1 << ($Double::BITS - 2 - sum_lz / 2 * 2);
|
||||
}
|
||||
while bit != 0 {
|
||||
let y_plus_bit = y + bit;
|
||||
y >>= 1;
|
||||
if x >= y_plus_bit {
|
||||
x -= y_plus_bit;
|
||||
y += bit;
|
||||
}
|
||||
bit >>= 2;
|
||||
}
|
||||
debug_assert!((y >> $Single::BITS) as $Single == overflow as $Single);
|
||||
(y as $Single, overflow)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_hypot! { u8, u16 }
|
||||
impl_hypot! { u16, u32, u8 }
|
||||
impl_hypot! { u32, u64, u16 }
|
||||
impl_hypot! { u64, u128, u32 }
|
||||
|
||||
pub const fn u128(a: u128, b: u128) -> (u128, bool) {
|
||||
if a <= (u64::MAX as u128) && b <= (u64::MAX as u128) {
|
||||
let val = match u64(a as u64, b as u64) {
|
||||
(val, false) => val as u128,
|
||||
(val, true) => (val as u128) + (1 << 64),
|
||||
};
|
||||
return (val, false);
|
||||
}
|
||||
|
||||
let aa = int256::wide_mul_u128(a, a);
|
||||
let bb = int256::wide_mul_u128(b, b);
|
||||
let (sum, overflow) = int256::overflowing_add_u256(aa, bb);
|
||||
|
||||
let mut x = sum;
|
||||
let mut y;
|
||||
let mut bit;
|
||||
if overflow {
|
||||
// Perform initialization and two iterations assuming word wider than n bits.
|
||||
// Initialization:
|
||||
// Set y = 0
|
||||
// Set bit = 1 << n
|
||||
// First iteration:
|
||||
// Since x >= y + bit (because the overflow is equivalent to 1 << n on x):
|
||||
// x -= y + bit (removing the effect of the overflow on x)
|
||||
// y = (y >> 1) + bit (y becomes 1 << n)
|
||||
// bit >>= 2 (bit becomes 1 << (n - 2))
|
||||
// Second iteration:
|
||||
// Now x < y (because y is 1 << n):
|
||||
// y >>= 1 (y becomes 1 << (n - 1))
|
||||
// bit >>= 2 (bit becomes 1 << (n - 4))
|
||||
// In effect, we can set y = 1 << (n - 1), and bit = 1 << (n - 4)
|
||||
y = U256 {
|
||||
lo: 0,
|
||||
hi: 1 << 127,
|
||||
};
|
||||
bit = 1 << 124;
|
||||
} else {
|
||||
y = U256 { lo: 0, hi: 0 };
|
||||
bit = match NonZero::<u128>::new(sum.hi) {
|
||||
None => panic!("small operands; should have used crate::hypot::u64"),
|
||||
Some(s) => 1 << (126 - s.leading_zeros() / 2 * 2),
|
||||
};
|
||||
}
|
||||
while bit != 0 {
|
||||
let y_hi_plus_bit = y.hi + bit;
|
||||
y.hi >>= 1;
|
||||
if x.hi >= y_hi_plus_bit {
|
||||
x.hi -= y_hi_plus_bit;
|
||||
y.hi += bit;
|
||||
}
|
||||
bit >>= 2;
|
||||
}
|
||||
bit = 1 << 126;
|
||||
while bit != 0 {
|
||||
let y_plus_bit = U256 {
|
||||
lo: y.lo + bit,
|
||||
hi: y.hi,
|
||||
};
|
||||
y.lo = (y.lo >> 1) + ((y.hi & 1) << 127);
|
||||
y.hi >>= 1;
|
||||
if (x.hi > y_plus_bit.hi) || (x.hi == y_plus_bit.hi && x.lo >= y_plus_bit.lo) {
|
||||
x = int256::wrapping_sub_u256(x, y_plus_bit);
|
||||
y.lo += bit;
|
||||
}
|
||||
bit >>= 2
|
||||
}
|
||||
|
||||
debug_assert!(y.hi == overflow as u128);
|
||||
(y.lo, overflow)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::hypot;
|
||||
use crate::types::{U1F7, U1F15, U1F31, U1F63, U1F127};
|
||||
|
||||
#[test]
|
||||
fn check_max() {
|
||||
assert_eq!(hypot::u8(u8::MAX, u8::MAX), (104, true));
|
||||
assert_eq!(hypot::u16(u16::MAX, u16::MAX), (27_144, true));
|
||||
assert_eq!(hypot::u32(u32::MAX, u32::MAX), (1_779_033_702, true));
|
||||
assert_eq!(
|
||||
hypot::u64(u64::MAX, u64::MAX),
|
||||
(7_640_891_576_956_012_807, true)
|
||||
);
|
||||
assert_eq!(
|
||||
hypot::u128(u128::MAX, u128::MAX),
|
||||
(140_949_571_415_070_559_626_692_937_523_481_902_396, true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_zero() {
|
||||
assert_eq!(hypot::u8(0, 0), (0, false));
|
||||
assert_eq!(hypot::u16(0, 0), (0, false));
|
||||
assert_eq!(hypot::u32(0, 0), (0, false));
|
||||
assert_eq!(hypot::u64(0, 0), (0, false));
|
||||
assert_eq!(hypot::u128(0, 0), (0, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_zero_max() {
|
||||
assert_eq!(hypot::u8(u8::MAX, 0), (u8::MAX, false));
|
||||
assert_eq!(hypot::u8(0, u8::MAX), (u8::MAX, false));
|
||||
assert_eq!(hypot::u16(u16::MAX, 0), (u16::MAX, false));
|
||||
assert_eq!(hypot::u16(0, u16::MAX), (u16::MAX, false));
|
||||
assert_eq!(hypot::u32(u32::MAX, 0), (u32::MAX, false));
|
||||
assert_eq!(hypot::u32(0, u32::MAX), (u32::MAX, false));
|
||||
assert_eq!(hypot::u64(u64::MAX, 0), (u64::MAX, false));
|
||||
assert_eq!(hypot::u64(0, u64::MAX), (u64::MAX, false));
|
||||
assert_eq!(hypot::u128(u128::MAX, 0), (u128::MAX, false));
|
||||
assert_eq!(hypot::u128(0, u128::MAX), (u128::MAX, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_max_plus() {
|
||||
// hypot(2^n - 1, x) = 2^n; x = sqrt(2^(n+1) - 1)
|
||||
// e.g. for u32, sqrt(2^33 - 1) = 92681.9
|
||||
assert_eq!(hypot::u8(u8::MAX, 22), (u8::MAX, false));
|
||||
assert_eq!(hypot::u8(u8::MAX, 23), (0, true));
|
||||
assert_eq!(hypot::u16(u16::MAX, 362), (u16::MAX, false));
|
||||
assert_eq!(hypot::u16(u16::MAX, 363), (0, true));
|
||||
assert_eq!(hypot::u32(u32::MAX, 92_681), (u32::MAX, false));
|
||||
assert_eq!(hypot::u32(u32::MAX, 92_682), (0, true));
|
||||
assert_eq!(hypot::u64(u64::MAX, 6_074_000_999), (u64::MAX, false));
|
||||
assert_eq!(hypot::u64(u64::MAX, 6_074_001_000), (0, true));
|
||||
assert_eq!(
|
||||
hypot::u128(u128::MAX, 26_087_635_650_665_564_424),
|
||||
(u128::MAX, false)
|
||||
);
|
||||
assert_eq!(
|
||||
hypot::u128(u128::MAX, 26_087_635_650_665_564_425),
|
||||
(0, true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_sqrt_2() {
|
||||
assert_eq!(hypot::u8(1 << 7, 1 << 7), (U1F7::SQRT_2.to_bits(), false));
|
||||
assert_eq!(
|
||||
hypot::u16(1 << 15, 1 << 15),
|
||||
(U1F15::SQRT_2.to_bits(), false)
|
||||
);
|
||||
assert_eq!(
|
||||
hypot::u32(1 << 31, 1 << 31),
|
||||
(U1F31::SQRT_2.to_bits(), false)
|
||||
);
|
||||
assert_eq!(
|
||||
hypot::u64(1 << 63, 1 << 63),
|
||||
(U1F63::SQRT_2.to_bits(), false)
|
||||
);
|
||||
assert_eq!(
|
||||
hypot::u128(1 << 127, 1 << 127),
|
||||
(U1F127::SQRT_2.to_bits(), false)
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::types::extra::{LeEqU8, LeEqU16, LeEqU32, LeEqU64, LeEqU128};
|
||||
use crate::{
|
||||
FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
|
||||
FixedU128, Unwrapped, Wrapping,
|
||||
};
|
||||
use arbitrary::{Arbitrary, Result as ArbitraryResult, Unstructured};
|
||||
|
||||
macro_rules! impl_trait {
|
||||
($Fixed:ident, $LeEqU:ident, $Inner:ident) => {
|
||||
impl<'a, Frac> Arbitrary<'a> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> ArbitraryResult<Self> {
|
||||
Ok(Self::from_bits(<$Inner as Arbitrary<'a>>::arbitrary(u)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(depth: usize) -> (usize, Option<usize>) {
|
||||
<$Inner as Arbitrary<'a>>::size_hint(depth)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Frac: $LeEqU> Arbitrary<'a> for Wrapping<$Fixed<Frac>> {
|
||||
#[inline]
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> ArbitraryResult<Self> {
|
||||
Ok(Self::from_bits(<$Inner as Arbitrary<'a>>::arbitrary(u)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(depth: usize) -> (usize, Option<usize>) {
|
||||
<$Inner as Arbitrary<'a>>::size_hint(depth)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Frac: $LeEqU> Arbitrary<'a> for Unwrapped<$Fixed<Frac>> {
|
||||
#[inline]
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> ArbitraryResult<Self> {
|
||||
Ok(Self::from_bits(<$Inner as Arbitrary<'a>>::arbitrary(u)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(depth: usize) -> (usize, Option<usize>) {
|
||||
<$Inner as Arbitrary<'a>>::size_hint(depth)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_trait! { FixedI8, LeEqU8, i8 }
|
||||
impl_trait! { FixedI16, LeEqU16, i16 }
|
||||
impl_trait! { FixedI32, LeEqU32, i32 }
|
||||
impl_trait! { FixedI64, LeEqU64, i64 }
|
||||
impl_trait! { FixedI128, LeEqU128, i128 }
|
||||
impl_trait! { FixedU8, LeEqU8, u8 }
|
||||
impl_trait! { FixedU16, LeEqU16, u16 }
|
||||
impl_trait! { FixedU32, LeEqU32, u32 }
|
||||
impl_trait! { FixedU64, LeEqU64, u64 }
|
||||
impl_trait! { FixedU128, LeEqU128, u128 }
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::types::extra::{LeEqU8, LeEqU16, LeEqU32, LeEqU64, LeEqU128};
|
||||
use crate::{
|
||||
FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
|
||||
FixedU128, Unwrapped, Wrapping,
|
||||
};
|
||||
use bytemuck::{Contiguous, Pod, TransparentWrapper, Zeroable};
|
||||
|
||||
macro_rules! unsafe_impl_traits {
|
||||
($Fixed:ident, $LeEqU:ident, $Inner:ident) => {
|
||||
unsafe impl<Frac> Zeroable for $Fixed<Frac> {}
|
||||
unsafe impl<Frac: 'static> Pod for $Fixed<Frac> {}
|
||||
unsafe impl<Frac: 'static> Contiguous for $Fixed<Frac> {
|
||||
type Int = $Inner;
|
||||
const MAX_VALUE: $Inner = $Inner::MAX;
|
||||
const MIN_VALUE: $Inner = $Inner::MIN;
|
||||
}
|
||||
unsafe impl<Frac> TransparentWrapper<$Inner> for $Fixed<Frac> {}
|
||||
|
||||
unsafe impl<Frac: $LeEqU> Zeroable for Wrapping<$Fixed<Frac>> {}
|
||||
unsafe impl<Frac: $LeEqU> Pod for Wrapping<$Fixed<Frac>> {}
|
||||
unsafe impl<Frac: $LeEqU> Contiguous for Wrapping<$Fixed<Frac>> {
|
||||
type Int = $Inner;
|
||||
const MAX_VALUE: $Inner = $Inner::MAX;
|
||||
const MIN_VALUE: $Inner = $Inner::MIN;
|
||||
}
|
||||
unsafe impl<Frac: $LeEqU> TransparentWrapper<$Fixed<Frac>> for Wrapping<$Fixed<Frac>> {}
|
||||
|
||||
unsafe impl<Frac: $LeEqU> Zeroable for Unwrapped<$Fixed<Frac>> {}
|
||||
unsafe impl<Frac: $LeEqU> Pod for Unwrapped<$Fixed<Frac>> {}
|
||||
unsafe impl<Frac: $LeEqU> Contiguous for Unwrapped<$Fixed<Frac>> {
|
||||
type Int = $Inner;
|
||||
const MAX_VALUE: $Inner = $Inner::MAX;
|
||||
const MIN_VALUE: $Inner = $Inner::MIN;
|
||||
}
|
||||
unsafe impl<Frac: $LeEqU> TransparentWrapper<$Fixed<Frac>> for Unwrapped<$Fixed<Frac>> {}
|
||||
};
|
||||
}
|
||||
|
||||
// SAFETY: all fixed-point numbers are repr(transparent) over primitive integer
|
||||
// types which are both Pod and Zeroable, and Wrapping and Unwrapped are both
|
||||
// repr(transparent) over fixed-point numbers.
|
||||
unsafe_impl_traits! { FixedI8, LeEqU8, i8 }
|
||||
unsafe_impl_traits! { FixedI16, LeEqU16, i16 }
|
||||
unsafe_impl_traits! { FixedI32, LeEqU32, i32 }
|
||||
unsafe_impl_traits! { FixedI64, LeEqU64, i64 }
|
||||
unsafe_impl_traits! { FixedI128, LeEqU128, i128 }
|
||||
unsafe_impl_traits! { FixedU8, LeEqU8, u8 }
|
||||
unsafe_impl_traits! { FixedU16, LeEqU16, u16 }
|
||||
unsafe_impl_traits! { FixedU32, LeEqU32, u32 }
|
||||
unsafe_impl_traits! { FixedU64, LeEqU64, u64 }
|
||||
unsafe_impl_traits! { FixedU128, LeEqU128, u128 }
|
||||
Vendored
+587
@@ -0,0 +1,587 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::consts;
|
||||
use crate::traits::Fixed;
|
||||
use crate::types::extra::{
|
||||
IsLessOrEqual, LeEqU8, LeEqU16, LeEqU32, LeEqU64, LeEqU128, True, U6, U7, U14, U15, U30, U31,
|
||||
U62, U63, U126, U127,
|
||||
};
|
||||
use crate::{
|
||||
FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
|
||||
FixedU128, ParseFixedError,
|
||||
};
|
||||
use core::fmt::{Display, Formatter, Result as FmtResult};
|
||||
use num_traits::bounds::Bounded;
|
||||
use num_traits::cast::{FromPrimitive, ToPrimitive};
|
||||
use num_traits::float::FloatConst;
|
||||
use num_traits::identities::{ConstOne, ConstZero, One, Zero};
|
||||
|
||||
use core::error::Error;
|
||||
use num_traits::Num;
|
||||
use num_traits::ops::bytes::{FromBytes, ToBytes};
|
||||
use num_traits::ops::checked::{
|
||||
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
|
||||
};
|
||||
use num_traits::ops::inv::Inv;
|
||||
use num_traits::ops::mul_add::{MulAdd, MulAddAssign};
|
||||
use num_traits::ops::overflowing::{OverflowingAdd, OverflowingMul, OverflowingSub};
|
||||
use num_traits::ops::saturating::{SaturatingAdd, SaturatingMul, SaturatingSub};
|
||||
use num_traits::ops::wrapping::{
|
||||
WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub,
|
||||
};
|
||||
use num_traits::sign::{Signed, Unsigned};
|
||||
|
||||
/// An error which can be returned when parsing a fixed-point number
|
||||
/// with a given radix.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RadixParseFixedError {
|
||||
/// The radix is not 2, 8, 10 or 16.
|
||||
UnsupportedRadix,
|
||||
/// The string could not be parsed as a fixed-point number.
|
||||
ParseFixedError(ParseFixedError),
|
||||
}
|
||||
|
||||
impl RadixParseFixedError {
|
||||
fn message(&self) -> &str {
|
||||
match self {
|
||||
RadixParseFixedError::UnsupportedRadix => "unsupported radix",
|
||||
RadixParseFixedError::ParseFixedError(e) => e.message(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RadixParseFixedError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
Display::fmt(self.message(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for RadixParseFixedError {
|
||||
fn description(&self) -> &str {
|
||||
self.message()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_traits {
|
||||
($Fixed:ident, $LeEqU:ident, $OneMaxFrac:ident, $Signedness:ident) => {
|
||||
impl<Frac> Bounded for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn min_value() -> Self {
|
||||
Self::MIN
|
||||
}
|
||||
#[inline]
|
||||
fn max_value() -> Self {
|
||||
Self::MAX
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> Zero for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn zero() -> Self {
|
||||
Self::ZERO
|
||||
}
|
||||
#[inline]
|
||||
fn is_zero(&self) -> bool {
|
||||
(*self).is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> ConstZero for $Fixed<Frac> {
|
||||
const ZERO: Self = Self::ZERO;
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> One for $Fixed<Frac>
|
||||
where
|
||||
Frac: IsLessOrEqual<$OneMaxFrac, Output = True>,
|
||||
{
|
||||
#[inline]
|
||||
fn one() -> Self {
|
||||
Self::ONE
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> ConstOne for $Fixed<Frac>
|
||||
where
|
||||
Frac: IsLessOrEqual<$OneMaxFrac, Output = True>,
|
||||
{
|
||||
const ONE: Self = Self::ONE;
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> Num for $Fixed<Frac>
|
||||
where
|
||||
Frac: IsLessOrEqual<$OneMaxFrac, Output = True>,
|
||||
{
|
||||
type FromStrRadixErr = RadixParseFixedError;
|
||||
|
||||
#[inline]
|
||||
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
|
||||
match radix {
|
||||
2 => Self::from_str_binary(str),
|
||||
8 => Self::from_str_octal(str),
|
||||
10 => str.parse(),
|
||||
16 => Self::from_str_hex(str),
|
||||
_ => return Err(RadixParseFixedError::UnsupportedRadix),
|
||||
}
|
||||
.map_err(RadixParseFixedError::ParseFixedError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> Inv for $Fixed<Frac> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn inv(self) -> Self::Output {
|
||||
self.recip()
|
||||
}
|
||||
}
|
||||
|
||||
if_signed! {
|
||||
$Signedness;
|
||||
impl<Frac: $LeEqU> Signed for $Fixed<Frac>
|
||||
where
|
||||
Frac: IsLessOrEqual<$OneMaxFrac, Output = True>,
|
||||
{
|
||||
#[inline]
|
||||
fn abs(&self) -> Self {
|
||||
(*self).abs()
|
||||
}
|
||||
#[inline]
|
||||
fn abs_sub(&self, other: &Self) -> Self {
|
||||
if *self < *other {
|
||||
Self::ZERO
|
||||
} else {
|
||||
*self - *other
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn signum(&self) -> Self {
|
||||
(*self).signum()
|
||||
}
|
||||
#[inline]
|
||||
fn is_positive(&self) -> bool {
|
||||
(*self).is_positive()
|
||||
}
|
||||
#[inline]
|
||||
fn is_negative(&self) -> bool {
|
||||
(*self).is_negative()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if_unsigned! {
|
||||
$Signedness;
|
||||
impl<Frac: $LeEqU> Unsigned for $Fixed<Frac>
|
||||
where
|
||||
Frac: IsLessOrEqual<$OneMaxFrac, Output = True>,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> CheckedAdd for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn checked_add(&self, v: &Self) -> Option<Self> {
|
||||
(*self).checked_add(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> CheckedSub for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn checked_sub(&self, v: &Self) -> Option<Self> {
|
||||
(*self).checked_sub(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> CheckedNeg for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn checked_neg(&self) -> Option<Self> {
|
||||
(*self).checked_neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> CheckedMul for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn checked_mul(&self, v: &Self) -> Option<Self> {
|
||||
(*self).checked_mul(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> CheckedDiv for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn checked_div(&self, v: &Self) -> Option<Self> {
|
||||
(*self).checked_div(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> CheckedRem for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn checked_rem(&self, v: &Self) -> Option<Self> {
|
||||
(*self).checked_rem(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> CheckedShl for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn checked_shl(&self, rhs: u32) -> Option<Self> {
|
||||
(*self).checked_shl(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> CheckedShr for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn checked_shr(&self, rhs: u32) -> Option<Self> {
|
||||
(*self).checked_shr(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> SaturatingAdd for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn saturating_add(&self, v: &Self) -> Self {
|
||||
(*self).saturating_add(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> SaturatingSub for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn saturating_sub(&self, v: &Self) -> Self {
|
||||
(*self).saturating_sub(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> SaturatingMul for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn saturating_mul(&self, v: &Self) -> Self {
|
||||
(*self).saturating_mul(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> WrappingAdd for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn wrapping_add(&self, v: &Self) -> Self {
|
||||
(*self).wrapping_add(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> WrappingSub for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn wrapping_sub(&self, v: &Self) -> Self {
|
||||
(*self).wrapping_sub(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> WrappingNeg for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn wrapping_neg(&self) -> Self {
|
||||
(*self).wrapping_neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> WrappingMul for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn wrapping_mul(&self, v: &Self) -> Self {
|
||||
(*self).wrapping_mul(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> WrappingShl for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn wrapping_shl(&self, rhs: u32) -> Self {
|
||||
(*self).wrapping_shl(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> WrappingShr for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn wrapping_shr(&self, rhs: u32) -> Self {
|
||||
(*self).wrapping_shr(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> OverflowingAdd for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn overflowing_add(&self, v: &Self) -> (Self, bool) {
|
||||
(*self).overflowing_add(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac> OverflowingSub for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn overflowing_sub(&self, v: &Self) -> (Self, bool) {
|
||||
(*self).overflowing_sub(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> OverflowingMul for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn overflowing_mul(&self, v: &Self) -> (Self, bool) {
|
||||
(*self).overflowing_mul(*v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac, MulFrac: $LeEqU> MulAdd<$Fixed<MulFrac>> for $Fixed<Frac> {
|
||||
type Output = $Fixed<Frac>;
|
||||
#[inline]
|
||||
fn mul_add(self, a: $Fixed<MulFrac>, b: $Fixed<Frac>) -> $Fixed<Frac> {
|
||||
self.mul_add(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac, MulFrac: $LeEqU> MulAddAssign<$Fixed<MulFrac>> for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn mul_add_assign(&mut self, a: $Fixed<MulFrac>, b: $Fixed<Frac>) {
|
||||
*self = self.mul_add(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> FloatConst for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn E() -> Self {
|
||||
consts::E.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn FRAC_1_PI() -> Self {
|
||||
consts::FRAC_1_PI.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn FRAC_1_SQRT_2() -> Self {
|
||||
consts::FRAC_1_SQRT_2.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn FRAC_2_PI() -> Self {
|
||||
consts::FRAC_2_PI.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn FRAC_2_SQRT_PI() -> Self {
|
||||
consts::FRAC_2_SQRT_PI.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn FRAC_PI_2() -> Self {
|
||||
consts::FRAC_PI_2.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn FRAC_PI_3() -> Self {
|
||||
consts::FRAC_PI_3.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn FRAC_PI_4() -> Self {
|
||||
consts::FRAC_PI_4.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn FRAC_PI_6() -> Self {
|
||||
consts::FRAC_PI_6.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn FRAC_PI_8() -> Self {
|
||||
consts::FRAC_PI_8.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn LN_10() -> Self {
|
||||
consts::LN_10.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn LN_2() -> Self {
|
||||
consts::LN_2.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn LOG10_E() -> Self {
|
||||
consts::LOG10_E.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn LOG2_E() -> Self {
|
||||
consts::LOG2_E.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn PI() -> Self {
|
||||
consts::PI.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn SQRT_2() -> Self {
|
||||
consts::SQRT_2.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn TAU() -> Self {
|
||||
consts::TAU.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn LOG10_2() -> Self {
|
||||
consts::LOG10_2.to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn LOG2_10() -> Self {
|
||||
consts::LOG2_10.to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> ToPrimitive for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn to_i64(&self) -> Option<i64> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_u64(&self) -> Option<u64> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_isize(&self) -> Option<isize> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_i8(&self) -> Option<i8> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_i16(&self) -> Option<i16> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_i32(&self) -> Option<i32> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_i128(&self) -> Option<i128> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_usize(&self) -> Option<usize> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_u8(&self) -> Option<u8> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_u16(&self) -> Option<u16> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_u32(&self) -> Option<u32> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_u128(&self) -> Option<u128> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_f32(&self) -> Option<f32> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
#[inline]
|
||||
fn to_f64(&self) -> Option<f64> {
|
||||
self.checked_to_num()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> FromPrimitive for $Fixed<Frac> {
|
||||
#[inline]
|
||||
fn from_i64(n: i64) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_u64(n: u64) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_isize(n: isize) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_i8(n: i8) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_i16(n: i16) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_i32(n: i32) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_i128(n: i128) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_usize(n: usize) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_u8(n: u8) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_u16(n: u16) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_u32(n: u32) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_u128(n: u128) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_f32(n: f32) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_f64(n: f64) -> Option<Self> {
|
||||
Self::checked_from_num(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> ToBytes for $Fixed<Frac> {
|
||||
type Bytes = <Self as Fixed>::Bytes;
|
||||
|
||||
fn to_be_bytes(&self) -> Self::Bytes {
|
||||
(*self).to_be_bytes()
|
||||
}
|
||||
|
||||
fn to_le_bytes(&self) -> Self::Bytes {
|
||||
(*self).to_le_bytes()
|
||||
}
|
||||
|
||||
fn to_ne_bytes(&self) -> Self::Bytes {
|
||||
(*self).to_ne_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> FromBytes for $Fixed<Frac> {
|
||||
type Bytes = <Self as Fixed>::Bytes;
|
||||
|
||||
fn from_be_bytes(bytes: &Self::Bytes) -> Self {
|
||||
Self::from_be_bytes(*bytes)
|
||||
}
|
||||
|
||||
fn from_le_bytes(bytes: &Self::Bytes) -> Self {
|
||||
Self::from_le_bytes(*bytes)
|
||||
}
|
||||
|
||||
fn from_ne_bytes(bytes: &Self::Bytes) -> Self {
|
||||
Self::from_ne_bytes(*bytes)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_traits! { FixedI8, LeEqU8, U6, Signed }
|
||||
impl_traits! { FixedI16, LeEqU16, U14, Signed }
|
||||
impl_traits! { FixedI32, LeEqU32, U30, Signed }
|
||||
impl_traits! { FixedI64, LeEqU64, U62, Signed }
|
||||
impl_traits! { FixedI128, LeEqU128, U126, Signed }
|
||||
impl_traits! { FixedU8, LeEqU8, U7, Unsigned }
|
||||
impl_traits! { FixedU16, LeEqU16, U15, Unsigned }
|
||||
impl_traits! { FixedU32, LeEqU32, U31, Unsigned }
|
||||
impl_traits! { FixedU64, LeEqU64, U63, Unsigned }
|
||||
impl_traits! { FixedU128, LeEqU128, U127, Unsigned }
|
||||
Vendored
+435
@@ -0,0 +1,435 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use core::hint;
|
||||
use core::num::NonZero;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct U256 {
|
||||
pub lo: u128,
|
||||
pub hi: u128,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct I256 {
|
||||
pub lo: u128,
|
||||
pub hi: i128,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn u256_wrapping_as_i256(a: U256) -> I256 {
|
||||
I256 {
|
||||
lo: a.lo,
|
||||
hi: a.hi as i128,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn wrapping_add_u256_u128(a: U256, b: u128) -> U256 {
|
||||
let (lo, carry) = a.lo.overflowing_add(b);
|
||||
let hi = a.hi.wrapping_add(carry as u128);
|
||||
U256 { lo, hi }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn overflowing_add_u128_u256(a: u128, b: U256) -> (u128, bool) {
|
||||
let (lo, carry) = a.overflowing_add(b.lo);
|
||||
(lo, carry | (b.hi != 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn overflowing_sub_u128_u256(a: u128, b: U256) -> (u128, bool) {
|
||||
let (lo, borrow) = a.overflowing_sub(b.lo);
|
||||
(lo, borrow | (b.hi != 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn wrapping_neg_u256(a: U256) -> U256 {
|
||||
let (lo, carry) = (!a.lo).overflowing_add(1);
|
||||
let hi = (!a.hi).wrapping_add(carry as u128);
|
||||
U256 { lo, hi }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn overflowing_add_i256_i128(a: I256, b: i128) -> (I256, bool) {
|
||||
let b = I256 {
|
||||
lo: b as u128,
|
||||
hi: b >> 127,
|
||||
};
|
||||
let (lo, carry) = a.lo.overflowing_add(b.lo);
|
||||
// b.hi is in {-1, 0}, and carry is in {0, 1}, so we can add them wrappingly
|
||||
let b_hi_plus_carry = b.hi.wrapping_add(carry as i128);
|
||||
let (hi, overflow) = a.hi.overflowing_add(b_hi_plus_carry);
|
||||
(I256 { lo, hi }, overflow)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn u128_lo_hi(u: u128) -> (u64, u64) {
|
||||
(u as u64, (u >> 64) as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn i128_lo_hi(i: i128) -> (u64, i64) {
|
||||
(i as u64, (i >> 64) as i64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn mul_u64_u64(a: u64, b: u64) -> u128 {
|
||||
(a as u128).wrapping_mul(b as u128)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn mul_i64_i64(a: i64, b: i64) -> i128 {
|
||||
(a as i128).wrapping_mul(b as i128)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn mul_u64_i64(a: u64, b: i64) -> i128 {
|
||||
let a = a as i64;
|
||||
// if a has become negative, we need to add 2^64 * b to the answer
|
||||
let correction = if a.is_negative() {
|
||||
(b as i128) << 64
|
||||
} else {
|
||||
0
|
||||
};
|
||||
(a as i128).wrapping_mul(b as i128).wrapping_add(correction)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn wide_mul_u128(lhs: u128, rhs: u128) -> U256 {
|
||||
let (ll, lh) = u128_lo_hi(lhs);
|
||||
let (rl, rh) = u128_lo_hi(rhs);
|
||||
// 0 <= ll_rl <= 2^128 - 2^65 + 1; ll_rl unit is 1
|
||||
let ll_rl = mul_u64_u64(ll, rl);
|
||||
// 0 <= lh_rl <= 2^128 - 2^65 + 1; lh_rl unit is 2^64
|
||||
let lh_rl = mul_u64_u64(lh, rl);
|
||||
// 0 <= ll_rh <= 2^128 - 2^65 + 1; ll_rh unit is 2^64
|
||||
let ll_rh = mul_u64_u64(ll, rh);
|
||||
// 0 <= lh_rh <= 2^128 - 2^65 + 1; lh_rh unit is 2^128
|
||||
let lh_rh = mul_u64_u64(lh, rh);
|
||||
|
||||
// 0 <= col0 <= 2^64 - 1
|
||||
// 0 <= col64a <= 2^64 - 2
|
||||
let (col0, col64a) = u128_lo_hi(ll_rl);
|
||||
|
||||
// 0 <= col64b <= 2^128 - 2^64 - 1
|
||||
let col64b = (col64a as u128).wrapping_add(lh_rl);
|
||||
|
||||
// 0 <= col64c <= 2^64 - 1
|
||||
// 0 <= col128a <= 2^64 - 2
|
||||
let (col64c, col128a) = u128_lo_hi(col64b);
|
||||
|
||||
// 0 <= col64d <= 2^128 - 2^64
|
||||
let col64d = (col64c as u128).wrapping_add(ll_rh);
|
||||
|
||||
// 0 <= col64 <= 2^64 - 1
|
||||
// 0 <= col128b <= 2^64 - 1
|
||||
let (col64, col128b) = u128_lo_hi(col64d);
|
||||
|
||||
// Since both col0 and col64 fit in 64 bits, ans0 sum will never overflow.
|
||||
let ans0 = (col0 as u128) | ((col64 as u128) << 64);
|
||||
// Since lhs * rhs fits in 256 bits, ans128 sum will never overflow.
|
||||
let ans128 = lh_rh
|
||||
.wrapping_add(col128a as u128)
|
||||
.wrapping_add(col128b as u128);
|
||||
U256 {
|
||||
lo: ans0,
|
||||
hi: ans128,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn wide_mul_i128(lhs: i128, rhs: i128) -> I256 {
|
||||
let (ll, lh) = i128_lo_hi(lhs);
|
||||
let (rl, rh) = i128_lo_hi(rhs);
|
||||
// 0 <= ll_rl <= 2^128 - 2^65 + 1; ll_rl unit is 1; must be unsigned to hold all range!
|
||||
let ll_rl = mul_u64_u64(ll, rl);
|
||||
// -2^127 + 2^63 <= lh_rl <= 2^127 - 2^64 - 2^63 + 1; lh_rl unit is 2^64
|
||||
let lh_rl = mul_u64_i64(rl, lh);
|
||||
// -2^127 + 2^63 <= ll_rh <= 2^127 - 2^64 - 2^63 + 1; ll_rh unit is 2^64
|
||||
let ll_rh = mul_u64_i64(ll, rh);
|
||||
// -2^126 + 2^63 <= lh_rh <= 2^126; lh_rh unit is 2^128
|
||||
let lh_rh = mul_i64_i64(lh, rh);
|
||||
|
||||
// 0 <= col0 <= 2^64 - 1
|
||||
// 0 <= col64a <= 2^64 - 2
|
||||
let (col0, col64a) = u128_lo_hi(ll_rl);
|
||||
|
||||
// -2^127 + 2^63 <= col64b <= 2^127 - 2^63 - 1
|
||||
let col64b = (col64a as i128).wrapping_add(lh_rl);
|
||||
|
||||
// 0 <= col64c <= 2^64 - 1
|
||||
// -2^63 <= col128a <= 2^63 - 1
|
||||
let (col64c, col128a) = i128_lo_hi(col64b);
|
||||
|
||||
// -2^127 + 2^63 <= col64d <= 2^127 - 2^63
|
||||
let col64d = (col64c as i128).wrapping_add(ll_rh);
|
||||
|
||||
// 0 <= col64 <= 2^64 - 1
|
||||
// -2^63 <= col128b <= 2^63 - 1
|
||||
let (col64, col128b) = i128_lo_hi(col64d);
|
||||
|
||||
// Since both col0 and col64 fit in 64 bits, ans0 sum will never overflow.
|
||||
let ans0 = (col0 as u128) | ((col64 as u128) << 64);
|
||||
// Since lhs * rhs fits in 256 bits, ans128 sum will never overflow.
|
||||
let ans128 = lh_rh
|
||||
.wrapping_add(col128a as i128)
|
||||
.wrapping_add(col128b as i128);
|
||||
I256 {
|
||||
lo: ans0,
|
||||
hi: ans128,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn shl_u256_max_128(a: U256, sh: u32) -> U256 {
|
||||
if sh == 0 {
|
||||
a
|
||||
} else if sh == 128 {
|
||||
U256 { lo: a.hi, hi: 0 }
|
||||
} else {
|
||||
U256 {
|
||||
lo: (a.lo >> sh) | (a.hi << (128 - sh)),
|
||||
hi: a.hi >> sh,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn shl_i256_max_128(a: I256, sh: u32) -> I256 {
|
||||
if sh == 0 {
|
||||
a
|
||||
} else if sh == 128 {
|
||||
I256 {
|
||||
lo: a.hi as u128,
|
||||
hi: a.hi >> 127,
|
||||
}
|
||||
} else {
|
||||
I256 {
|
||||
lo: (a.lo >> sh) | (a.hi << (128 - sh)) as u128,
|
||||
hi: a.hi >> sh,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// d must have msb set.
|
||||
#[inline]
|
||||
const unsafe fn div_half_u128(r: u128, d: u128, next_half: u64) -> (u128, u128) {
|
||||
let (dl, dh) = u128_lo_hi(d);
|
||||
// SAFETY: we know d has the most significant bit set because we normalized
|
||||
unsafe {
|
||||
hint::assert_unchecked(dh != 0);
|
||||
}
|
||||
let (mut q, rr) = (r / (dh as u128), r % (dh as u128));
|
||||
let m = q * (dl as u128);
|
||||
let mut r = (next_half as u128) + (rr << 64);
|
||||
if r < m {
|
||||
q -= 1;
|
||||
let (new_r, overflow) = r.overflowing_add(d);
|
||||
r = if !overflow && new_r < m {
|
||||
q -= 1;
|
||||
new_r.wrapping_add(d)
|
||||
} else {
|
||||
new_r
|
||||
};
|
||||
}
|
||||
r = r.wrapping_sub(m);
|
||||
(r, q)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn div_rem_u256_u128(mut n: U256, d: NonZero<u128>) -> (U256, u128) {
|
||||
let zeros = d.leading_zeros();
|
||||
let (mut r, d) = if zeros == 0 {
|
||||
(0, d.get())
|
||||
} else {
|
||||
let n2 = n.hi >> (128 - zeros);
|
||||
n.hi = (n.hi << zeros) | (n.lo >> (128 - zeros));
|
||||
n.lo <<= zeros;
|
||||
(n2, d.get() << zeros)
|
||||
};
|
||||
|
||||
// SAFETY: we know that d has msb set because it is not zero and it was
|
||||
// shifted to the right by the number of leading zeros.
|
||||
let (nhl, nhh) = u128_lo_hi(n.hi);
|
||||
let (new_r, qhh) = unsafe { div_half_u128(r, d, nhh) };
|
||||
r = new_r;
|
||||
let (new_r, qhl) = unsafe { div_half_u128(r, d, nhl) };
|
||||
r = new_r;
|
||||
let (nll, nlh) = u128_lo_hi(n.lo);
|
||||
let (new_r, qlh) = unsafe { div_half_u128(r, d, nlh) };
|
||||
r = new_r;
|
||||
let (new_r, qll) = unsafe { div_half_u128(r, d, nll) };
|
||||
r = new_r;
|
||||
let q = U256 {
|
||||
lo: qll + (qlh << 64),
|
||||
hi: qhl + (qhh << 64),
|
||||
};
|
||||
r >>= zeros;
|
||||
(q, r)
|
||||
}
|
||||
|
||||
// must not result in overflow
|
||||
#[inline]
|
||||
pub const fn div_rem_i256_i128_no_overflow(n: I256, d: NonZero<i128>) -> (I256, i128) {
|
||||
let (n_neg, n_abs) = if n.hi < 0 {
|
||||
let (nl, overflow) = n.lo.overflowing_neg();
|
||||
let nh = n.hi.wrapping_neg().wrapping_sub(overflow as i128) as u128;
|
||||
(true, U256 { lo: nl, hi: nh })
|
||||
} else {
|
||||
let nl = n.lo;
|
||||
let nh = n.hi as u128;
|
||||
(false, U256 { lo: nl, hi: nh })
|
||||
};
|
||||
let (d_neg, d_abs) = if d.get() < 0 {
|
||||
// SAFETY: d and -d are not zero
|
||||
let ud = unsafe { NonZero::<u128>::new_unchecked(d.get().wrapping_neg() as u128) };
|
||||
(true, ud)
|
||||
} else {
|
||||
// SAFETY: d is not zero
|
||||
let ud = unsafe { NonZero::<u128>::new_unchecked(d.get() as u128) };
|
||||
(false, ud)
|
||||
};
|
||||
|
||||
let (q_abs, r_abs) = div_rem_u256_u128(n_abs, d_abs);
|
||||
|
||||
let q = if n_neg == d_neg {
|
||||
let ql = q_abs.lo;
|
||||
let qh = q_abs.hi as i128;
|
||||
I256 { lo: ql, hi: qh }
|
||||
} else {
|
||||
let (ql, overflow) = q_abs.lo.overflowing_neg();
|
||||
let qh = q_abs.hi.wrapping_neg().wrapping_sub(overflow as u128) as i128;
|
||||
I256 { lo: ql, hi: qh }
|
||||
};
|
||||
let r = if n_neg {
|
||||
r_abs.wrapping_neg() as i128
|
||||
} else {
|
||||
r_abs as i128
|
||||
};
|
||||
(q, r)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn overflowing_add_u256(a: U256, b: U256) -> (U256, bool) {
|
||||
let (lo, carry0) = a.lo.overflowing_add(b.lo);
|
||||
let (hi, carry1_a) = a.hi.overflowing_add(b.hi);
|
||||
let (hi, carry1_b) = hi.overflowing_add(carry0 as u128);
|
||||
(U256 { lo, hi }, carry1_a | carry1_b)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn wrapping_sub_u256(a: U256, b: U256) -> U256 {
|
||||
let (lo, borrow) = a.lo.overflowing_sub(b.lo);
|
||||
let hi = a.hi.wrapping_sub(b.hi).wrapping_sub(borrow as u128);
|
||||
U256 { lo, hi }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn overflowing_shl_u256_into_u128(a: U256, sh: u32) -> (u128, bool) {
|
||||
if sh == 128 {
|
||||
(a.hi, false)
|
||||
} else if sh == 0 {
|
||||
(a.lo, a.hi != 0)
|
||||
} else {
|
||||
let lo = a.lo >> sh;
|
||||
let hi = a.hi << (128 - sh);
|
||||
(lo | hi, a.hi >> sh != 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn overflowing_shl_i256_into_i128(a: I256, sh: u32) -> (i128, bool) {
|
||||
if sh == 128 {
|
||||
(a.hi, false)
|
||||
} else if sh == 0 {
|
||||
let ans = a.lo as i128;
|
||||
(ans, a.hi != ans >> 127)
|
||||
} else {
|
||||
let lo = (a.lo >> sh) as i128;
|
||||
let hi = a.hi << (128 - sh);
|
||||
let ans = lo | hi;
|
||||
(ans, a.hi >> sh != ans >> 127)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn check_udiv_rem(num: U256, den: u128) {
|
||||
let (quot, rem) = div_rem_u256_u128(num, NonZero::<u128>::new(den).unwrap());
|
||||
assert!(rem <= den);
|
||||
|
||||
let ql_d = wide_mul_u128(quot.lo, den);
|
||||
let qh_d = wide_mul_u128(quot.hi, den);
|
||||
assert!(qh_d.hi == 0);
|
||||
let prod_lo = ql_d.lo;
|
||||
let prod_hi = ql_d.hi.checked_add(qh_d.lo).unwrap();
|
||||
let (sum_lo, carry) = prod_lo.overflowing_add(rem);
|
||||
let sum_hi = prod_hi.checked_add(u128::from(carry)).unwrap();
|
||||
assert!(sum_lo == num.lo && sum_hi == num.hi);
|
||||
}
|
||||
|
||||
fn check_idiv_rem_signs(num: I256, den: i128) {
|
||||
let (quot, rem) = div_rem_i256_i128_no_overflow(num, NonZero::<i128>::new(den).unwrap());
|
||||
assert!(rem.unsigned_abs() <= den.unsigned_abs());
|
||||
|
||||
if num.hi < 0 {
|
||||
assert!(rem <= 0);
|
||||
} else {
|
||||
assert!(rem >= 0);
|
||||
}
|
||||
|
||||
if (num.hi < 0) == (den < 0) {
|
||||
assert!(quot.hi >= 0);
|
||||
} else {
|
||||
assert!(quot.hi <= 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_udiv_rem() {
|
||||
for d in 1u8..=255 {
|
||||
for n1 in (0u8..=255).step_by(15) {
|
||||
for n0 in (0u8..=255).step_by(15) {
|
||||
let d = (u128::from(d) << 120) | 1;
|
||||
let n1 = (u128::from(n1) << 120) | 1;
|
||||
let n0 = (u128::from(n0) << 120) | 1;
|
||||
check_udiv_rem(U256 { lo: n0, hi: n1 }, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_idiv_rem_signs() {
|
||||
for d in -128..=127 {
|
||||
if d == 0 {
|
||||
continue;
|
||||
}
|
||||
for n1 in (-120..=120).step_by(15) {
|
||||
for n0 in (0u8..=255).step_by(15) {
|
||||
let d = (i128::from(d) << 120) | 1;
|
||||
let n1 = (i128::from(n1) << 120) | 1;
|
||||
let n0 = (u128::from(n0) << 120) | 1;
|
||||
check_idiv_rem_signs(I256 { lo: n0, hi: n1 }, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+207
@@ -0,0 +1,207 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::types::extra::U0;
|
||||
use crate::{
|
||||
FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
|
||||
FixedU128,
|
||||
};
|
||||
|
||||
macro_rules! make_helper_common {
|
||||
($t:ident) => {
|
||||
use crate::helpers::{ToFixedHelper, Widest};
|
||||
use core::cmp::Ordering;
|
||||
};
|
||||
}
|
||||
macro_rules! make_helper {
|
||||
($i: ident, $u:ident) => {
|
||||
pub mod $i {
|
||||
make_helper_common! { $i }
|
||||
|
||||
#[inline]
|
||||
pub const fn neg_abs(val: $i) -> (bool, $u) {
|
||||
(val.is_negative(), val.unsigned_abs())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_fixed_helper(
|
||||
val: $i,
|
||||
src_frac_bits: i32,
|
||||
dst_frac_bits: u32,
|
||||
dst_int_bits: u32,
|
||||
) -> ToFixedHelper {
|
||||
let src_bits = $i::BITS as i32;
|
||||
let dst_bits = (dst_frac_bits + dst_int_bits) as i32;
|
||||
|
||||
if val == 0 {
|
||||
return ToFixedHelper {
|
||||
bits: Widest::Unsigned(0),
|
||||
dir: Ordering::Equal,
|
||||
overflow: false,
|
||||
};
|
||||
}
|
||||
|
||||
let need_to_shr = src_frac_bits - dst_frac_bits as i32;
|
||||
let leading = if val >= 0 {
|
||||
val.leading_zeros()
|
||||
} else {
|
||||
(!val).leading_zeros() - 1
|
||||
};
|
||||
let overflow = src_bits - dst_bits > need_to_shr + leading as i32;
|
||||
let bits_128 = i128::from(val);
|
||||
let (bits, lost_bits) = match need_to_shr {
|
||||
-0x7fff_ffff..=-128 => (0, false),
|
||||
-127..=-1 => (bits_128 << -need_to_shr, false),
|
||||
0 => (bits_128, false),
|
||||
1..=127 => {
|
||||
let shifted = bits_128 >> need_to_shr;
|
||||
(shifted, shifted << need_to_shr != bits_128)
|
||||
}
|
||||
128..=0x7fff_ffff => (bits_128 >> 127, true),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let dir = if lost_bits {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Equal
|
||||
};
|
||||
let bits = if val >= 0 {
|
||||
Widest::Unsigned(bits as u128)
|
||||
} else {
|
||||
Widest::Negative(bits)
|
||||
};
|
||||
ToFixedHelper {
|
||||
bits,
|
||||
dir,
|
||||
overflow,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod $u {
|
||||
make_helper_common! { $u }
|
||||
|
||||
#[inline]
|
||||
pub fn neg_abs(val: $u) -> (bool, $u) {
|
||||
(false, val)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_fixed_helper(
|
||||
val: $u,
|
||||
src_frac_bits: i32,
|
||||
dst_frac_bits: u32,
|
||||
dst_int_bits: u32,
|
||||
) -> ToFixedHelper {
|
||||
let src_bits = $u::BITS as i32;
|
||||
let dst_bits = (dst_frac_bits + dst_int_bits) as i32;
|
||||
|
||||
if val == 0 {
|
||||
return ToFixedHelper {
|
||||
bits: Widest::Unsigned(0),
|
||||
dir: Ordering::Equal,
|
||||
overflow: false,
|
||||
};
|
||||
}
|
||||
|
||||
let leading_zeros = val.leading_zeros();
|
||||
let need_to_shr = src_frac_bits - dst_frac_bits as i32;
|
||||
let overflow = src_bits - dst_bits > need_to_shr + leading_zeros as i32;
|
||||
let bits_128 = u128::from(val);
|
||||
let (bits, lost_bits) = match need_to_shr {
|
||||
-0x7fff_ffff..=-128 => (0, false),
|
||||
-127..=-1 => (bits_128 << -need_to_shr, false),
|
||||
0 => (bits_128, false),
|
||||
1..=127 => {
|
||||
let shifted = bits_128 >> need_to_shr;
|
||||
(shifted, shifted << need_to_shr != bits_128)
|
||||
}
|
||||
128..=0x7fff_ffff => (0, true),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let dir = if lost_bits {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Equal
|
||||
};
|
||||
ToFixedHelper {
|
||||
bits: Widest::Unsigned(bits),
|
||||
dir,
|
||||
overflow,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
make_helper! { i8, u8 }
|
||||
make_helper! { i16, u16 }
|
||||
make_helper! { i32, u32 }
|
||||
make_helper! { i64, u64 }
|
||||
make_helper! { i128, u128 }
|
||||
|
||||
pub struct IntFixed<T>(pub T);
|
||||
|
||||
macro_rules! make_int_fixed {
|
||||
($I:ident -> $F:ident) => {
|
||||
impl IntFixed<$I> {
|
||||
#[inline]
|
||||
pub fn fixed(self) -> $F<U0> {
|
||||
$F::<U0>::from_bits(self.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn int(f: $F<U0>) -> $I {
|
||||
f.to_bits()
|
||||
}
|
||||
}
|
||||
};
|
||||
($T:ident as $I:ident -> $F:ident) => {
|
||||
impl IntFixed<$T> {
|
||||
#[inline]
|
||||
pub fn fixed(self) -> $F<U0> {
|
||||
$F::<U0>::from_bits(self.0 as $I)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn int(f: $F<U0>) -> $T {
|
||||
f.to_bits() as $T
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
make_int_fixed! { i8 -> FixedI8 }
|
||||
make_int_fixed! { i16 -> FixedI16 }
|
||||
make_int_fixed! { i32 -> FixedI32 }
|
||||
make_int_fixed! { i64 -> FixedI64 }
|
||||
make_int_fixed! { i128 -> FixedI128 }
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
make_int_fixed! { isize as i16 -> FixedI16 }
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
make_int_fixed! { isize as i32 -> FixedI32 }
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
make_int_fixed! { isize as i64 -> FixedI64 }
|
||||
make_int_fixed! { u8 -> FixedU8 }
|
||||
make_int_fixed! { u16 -> FixedU16 }
|
||||
make_int_fixed! { u32 -> FixedU32 }
|
||||
make_int_fixed! { u64 -> FixedU64 }
|
||||
make_int_fixed! { u128 -> FixedU128 }
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
make_int_fixed! { usize as u16 -> FixedU16 }
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
make_int_fixed! { usize as u32 -> FixedU32 }
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
make_int_fixed! { usize as u64 -> FixedU64 }
|
||||
Vendored
+306
@@ -0,0 +1,306 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::int256;
|
||||
use crate::int256::U256;
|
||||
use core::num::NonZeroU128;
|
||||
|
||||
macro_rules! make_inv_lerp {
|
||||
($i:ident, $u:ident, $ii:ident, $uu:ident, $uuf:expr) => {
|
||||
pub const fn $i(v: $i, start: $i, end: $i, frac_bits: u32) -> ($i, bool) {
|
||||
assert!(start != end, "empty range");
|
||||
// 0x00 ≤ diff_abs ≤ 0xff
|
||||
let (diff_abs, diff_neg) = if v >= start {
|
||||
(v.wrapping_sub(start) as $u, false)
|
||||
} else {
|
||||
(start.wrapping_sub(v) as $u, true)
|
||||
};
|
||||
let wide_diff_abs = (diff_abs as $uu) << $u::BITS;
|
||||
// 0x01 ≤ range_abs ≤ 0xff
|
||||
let (range_abs, range_neg) = if end >= start {
|
||||
(end.wrapping_sub(start) as $u, false)
|
||||
} else {
|
||||
(start.wrapping_sub(end) as $u, true)
|
||||
};
|
||||
let neg = diff_neg != range_neg;
|
||||
let (wide_ret, overflow1) = if neg {
|
||||
// ensure that difference is not 0
|
||||
if diff_abs == 0 {
|
||||
return (0, false);
|
||||
}
|
||||
// 0x0001 ≤ ceil_div ≤ 0xff00
|
||||
let ceil_div = (wide_diff_abs - 1) / (range_abs as $uu) + 1;
|
||||
// 0x0000 ≤ add ≤ 0x00ff
|
||||
let add = $uuf >> frac_bits;
|
||||
// If frac_bits is 0: add = 0x00ff; 0x0000 ≤ shifted ≤ 0x00ff
|
||||
// If frac_bits is max: add = 0x0000; 0x0000 ≤ shifted ≤ 0xff00
|
||||
let shifted = (ceil_div + add) >> ($u::BITS - frac_bits);
|
||||
let wide_ret = shifted.wrapping_neg() as $ii;
|
||||
(wide_ret, wide_ret > 0)
|
||||
} else {
|
||||
// 0x0000 ≤ floor_div ≤ 0xff00
|
||||
let floor_div = wide_diff_abs / (range_abs as $uu);
|
||||
let shifted = floor_div >> ($u::BITS - frac_bits);
|
||||
let wide_ret = shifted as $ii;
|
||||
(wide_ret, wide_ret < 0)
|
||||
};
|
||||
let ret = wide_ret as $i;
|
||||
let overflow2 = wide_ret != ret as $ii;
|
||||
(ret, overflow1 | overflow2)
|
||||
}
|
||||
|
||||
pub const fn $u(v: $u, start: $u, end: $u, frac_bits: u32) -> ($u, bool) {
|
||||
assert!(start != end, "empty range");
|
||||
// 0x00 ≤ diff ≤ 0xff
|
||||
let (diff_abs, diff_neg) = if v >= start {
|
||||
(v - start, false)
|
||||
} else {
|
||||
(start - v, true)
|
||||
};
|
||||
let wide_diff_abs = (diff_abs as $uu) << $u::BITS;
|
||||
// 0x01 ≤ range_abs ≤ 0xff
|
||||
let (range_abs, range_neg) = if end >= start {
|
||||
(end - start, false)
|
||||
} else {
|
||||
(start - end, true)
|
||||
};
|
||||
let neg = diff_neg != range_neg;
|
||||
let (wide_ret, overflow1) = if neg {
|
||||
// ensure that difference is not 0
|
||||
if diff_abs == 0 {
|
||||
return (0, false);
|
||||
}
|
||||
// 0x0001 ≤ ceil_div ≤ 0xff00
|
||||
let ceil_div = (wide_diff_abs - 1) / (range_abs as $uu) + 1;
|
||||
// 0x0000 ≤ add ≤ 0x00ff
|
||||
let add = $uuf >> frac_bits;
|
||||
// If frac_bits is 0: add = 0x00ff; 0x0000 ≤ shifted ≤ 0x00ff
|
||||
// If frac_bits is max: add = 0x0000; 0x0000 ≤ shifted ≤ 0xff00
|
||||
let shifted = (ceil_div + add) >> ($u::BITS - frac_bits);
|
||||
let wide_ret = shifted.wrapping_neg();
|
||||
(wide_ret, wide_ret > 0)
|
||||
} else {
|
||||
// 0x0000 ≤ floor_div ≤ 0xff00
|
||||
let floor_div = wide_diff_abs / (range_abs as $uu);
|
||||
let shifted = floor_div >> ($u::BITS - frac_bits);
|
||||
(shifted, false)
|
||||
};
|
||||
let ret = wide_ret as $u;
|
||||
let overflow2 = wide_ret != ret as $uu;
|
||||
(ret, overflow1 | overflow2)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
make_inv_lerp! { i8, u8, i16, u16, 0xff_u16 }
|
||||
make_inv_lerp! { i16, u16, i32, u32, 0xffff_u32 }
|
||||
make_inv_lerp! { i32, u32, i64, u64, 0xffff_ffff_u64 }
|
||||
make_inv_lerp! { i64, u64, i128, u128, 0xffff_ffff_ffff_ffff_u128 }
|
||||
|
||||
pub const fn i128(v: i128, start: i128, end: i128, frac_bits: u32) -> (i128, bool) {
|
||||
// 0x00 ≤ diff_abs ≤ 0xff
|
||||
let (diff_abs, diff_neg) = if v >= start {
|
||||
(v.wrapping_sub(start) as u128, false)
|
||||
} else {
|
||||
(start.wrapping_sub(v) as u128, true)
|
||||
};
|
||||
let wide_diff_abs = U256 {
|
||||
lo: 0,
|
||||
hi: diff_abs,
|
||||
};
|
||||
// 0x01 ≤ range_abs ≤ 0xff
|
||||
let (range_abs, range_neg) = if end >= start {
|
||||
(end.wrapping_sub(start) as u128, false)
|
||||
} else {
|
||||
(start.wrapping_sub(end) as u128, true)
|
||||
};
|
||||
let Some(range_abs) = NonZeroU128::new(range_abs) else {
|
||||
panic!("empty range");
|
||||
};
|
||||
let neg = diff_neg != range_neg;
|
||||
if neg {
|
||||
// ensure that difference is not 0
|
||||
if diff_abs == 0 {
|
||||
return (0, false);
|
||||
}
|
||||
// 0x0001 ≤ ceil_div ≤ 0xff00
|
||||
let (div, rem) = int256::div_rem_u256_u128(wide_diff_abs, range_abs);
|
||||
let ceil_div = if rem > 0 {
|
||||
int256::wrapping_add_u256_u128(div, 1)
|
||||
} else {
|
||||
div
|
||||
};
|
||||
// 0x0000 ≤ add ≤ 0x00ff
|
||||
let add = if frac_bits == 128 {
|
||||
0
|
||||
} else {
|
||||
u128::MAX >> frac_bits
|
||||
};
|
||||
// If frac_bits is 0: add = 0x00ff; 0x0000 ≤ shifted ≤ 0x00ff
|
||||
// If frac_bits is max: add = 0x0000; 0x0000 ≤ shifted ≤ 0xff00
|
||||
let sum = int256::wrapping_add_u256_u128(ceil_div, add);
|
||||
let shifted = int256::shl_u256_max_128(sum, 128 - frac_bits);
|
||||
let neg_shifted = int256::wrapping_neg_u256(shifted);
|
||||
let ret = neg_shifted.lo as i128;
|
||||
let overflow = if ret.is_negative() {
|
||||
neg_shifted.hi != u128::MAX
|
||||
} else if ret == 0 {
|
||||
neg_shifted.hi != 0
|
||||
} else {
|
||||
true
|
||||
};
|
||||
(ret, overflow)
|
||||
} else {
|
||||
// 0x0000 ≤ floor_div ≤ 0xff00
|
||||
let (floor_div, _) = int256::div_rem_u256_u128(wide_diff_abs, range_abs);
|
||||
let shifted = int256::shl_u256_max_128(floor_div, 128 - frac_bits);
|
||||
let ret = shifted.lo as i128;
|
||||
let overflow = (ret < 0) | (shifted.hi != 0);
|
||||
(ret, overflow)
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn u128(v: u128, start: u128, end: u128, frac_bits: u32) -> (u128, bool) {
|
||||
// 0x00 ≤ diff ≤ 0xff
|
||||
let (diff_abs, diff_neg) = if v >= start {
|
||||
(v - start, false)
|
||||
} else {
|
||||
(start - v, true)
|
||||
};
|
||||
let wide_diff_abs = U256 {
|
||||
lo: 0,
|
||||
hi: diff_abs,
|
||||
};
|
||||
// 0x01 ≤ range_abs ≤ 0xff
|
||||
let (range_abs, range_neg) = if end >= start {
|
||||
(end - start, false)
|
||||
} else {
|
||||
(start - end, true)
|
||||
};
|
||||
let Some(range_abs) = NonZeroU128::new(range_abs) else {
|
||||
panic!("empty range");
|
||||
};
|
||||
let neg = diff_neg != range_neg;
|
||||
if neg {
|
||||
// ensure that difference is not 0
|
||||
if diff_abs == 0 {
|
||||
return (0, false);
|
||||
}
|
||||
// 0x0001 ≤ ceil_div ≤ 0xff00
|
||||
let (div, rem) = int256::div_rem_u256_u128(wide_diff_abs, range_abs);
|
||||
let ceil_div = if rem > 0 {
|
||||
int256::wrapping_add_u256_u128(div, 1)
|
||||
} else {
|
||||
div
|
||||
};
|
||||
// 0x0000 ≤ add ≤ 0x00ff
|
||||
let add = if frac_bits == 128 {
|
||||
0
|
||||
} else {
|
||||
u128::MAX >> frac_bits
|
||||
};
|
||||
// If frac_bits is 0: add = 0x00ff; 0x0000 ≤ shifted ≤ 0x00ff
|
||||
// If frac_bits is max: add = 0x0000; 0x0000 ≤ shifted ≤ 0xff00
|
||||
let sum = int256::wrapping_add_u256_u128(ceil_div, add);
|
||||
let shifted = int256::shl_u256_max_128(sum, 128 - frac_bits);
|
||||
let neg_shifted = int256::wrapping_neg_u256(shifted);
|
||||
let ret = neg_shifted.lo;
|
||||
let overflow = (ret != 0) | (neg_shifted.hi != 0);
|
||||
(ret, overflow)
|
||||
} else {
|
||||
// 0x0000 ≤ floor_div ≤ 0xff00
|
||||
let (floor_div, _) = int256::div_rem_u256_u128(wide_diff_abs, range_abs);
|
||||
let shifted = int256::shl_u256_max_128(floor_div, 128 - frac_bits);
|
||||
let ret = shifted.lo;
|
||||
let overflow = shifted.hi != 0;
|
||||
(ret, overflow)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::inv_lerp;
|
||||
|
||||
#[test]
|
||||
fn inv_lerp_i8() {
|
||||
assert_eq!(inv_lerp::i8(-128, -128, 127, 0), (0, false));
|
||||
assert_eq!(inv_lerp::i8(127, 127, -128, 8), (0, false));
|
||||
assert_eq!(inv_lerp::i8(-1, -128, 127, 6), (31, false));
|
||||
assert_eq!(inv_lerp::i8(-90, -100, -110, 6), (-64, false));
|
||||
|
||||
assert_eq!(inv_lerp::i8(50, 0, 2, 2), (100, false));
|
||||
assert_eq!(inv_lerp::i8(100, 0, 2, 2), (-56, true));
|
||||
assert_eq!(inv_lerp::i8(-26, -1, 0, 2), (-100, false));
|
||||
assert_eq!(inv_lerp::i8(50, 0, -1, 2), (56, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inv_lerp_u8() {
|
||||
assert_eq!(inv_lerp::u8(0, 0, 255, 0), (0, false));
|
||||
assert_eq!(inv_lerp::u8(255, 255, 0, 8), (0, false));
|
||||
assert_eq!(inv_lerp::u8(255, 0, 255, 0), (1, false));
|
||||
assert_eq!(inv_lerp::u8(128, 255, 0, 7), (63, false));
|
||||
assert_eq!(inv_lerp::u8(51, 52, 53, 6), (192, true));
|
||||
|
||||
assert_eq!(inv_lerp::u8(100, 0, 2, 2), (200, false));
|
||||
assert_eq!(inv_lerp::u8(200, 0, 2, 2), (200 - 56, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inv_lerp_i128() {
|
||||
assert_eq!(inv_lerp::i128(-128, -128, 127, 0), (0, false));
|
||||
assert_eq!(inv_lerp::i128(127, 127, -128, 128), (0, false));
|
||||
assert_eq!(inv_lerp::i128(-1, -128, 127, 6), (31, false));
|
||||
assert_eq!(inv_lerp::i128(-90, -100, -110, 126), ((-64) << 120, false));
|
||||
|
||||
assert_eq!(
|
||||
inv_lerp::i128(i128::MAX / 2, 0, 2, 2),
|
||||
(i128::MAX / 2 * 2, false)
|
||||
);
|
||||
assert_eq!(
|
||||
inv_lerp::i128(i128::MAX, 0, 2, 2),
|
||||
(i128::MAX.wrapping_mul(2), true)
|
||||
);
|
||||
assert_eq!(
|
||||
inv_lerp::i128(i128::MIN / 4 - 1, -1, 0, 2),
|
||||
(i128::MIN, false)
|
||||
);
|
||||
assert_eq!(
|
||||
inv_lerp::i128(i128::MAX, 0, -1, 2),
|
||||
(i128::MAX.wrapping_mul(-4), true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inv_lerp_u128() {
|
||||
assert_eq!(inv_lerp::u128(0, 0, 255, 0), (0, false));
|
||||
assert_eq!(inv_lerp::u128(255, 255, 0, 128), (0, false));
|
||||
assert_eq!(inv_lerp::u128(255, 0, 255, 0), (1, false));
|
||||
assert_eq!(
|
||||
inv_lerp::u128(1 << 127, u128::MAX, 0, 127),
|
||||
((1 << 126) - 1, false)
|
||||
);
|
||||
assert_eq!(inv_lerp::u128(51, 52, 53, 126), (192 << 120, true));
|
||||
|
||||
assert_eq!(
|
||||
inv_lerp::u128(u128::MAX / 2, 0, 2, 2),
|
||||
(u128::MAX / 2 * 2, false)
|
||||
);
|
||||
assert_eq!(
|
||||
inv_lerp::u128(u128::MAX, 0, 2, 2),
|
||||
(u128::MAX.wrapping_mul(2), true)
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+224
@@ -0,0 +1,224 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::int256;
|
||||
|
||||
macro_rules! make_lerp {
|
||||
($i:ident, $u:ident, $ii:ident, $uu:ident, $uu1:expr) => {
|
||||
pub const fn $i(r: $i, start: $i, end: $i, frac_bits: u32) -> ($i, bool) {
|
||||
// 0x00 ≤ r_abs ≤ 0x80
|
||||
let (r_abs, r_neg) = if r >= 0 {
|
||||
(r as $u, false)
|
||||
} else {
|
||||
(r.wrapping_neg() as $u, true)
|
||||
};
|
||||
// 0x00 ≤ range_abs ≤ 0xff
|
||||
let (range_abs, range_neg) = if end >= start {
|
||||
(end.wrapping_sub(start) as $u, false)
|
||||
} else {
|
||||
(start.wrapping_sub(end) as $u, true)
|
||||
};
|
||||
// 0x0000 ≤ wide_abs ≤ 0x7f80
|
||||
let wide_abs = (r_abs as $uu) * (range_abs as $uu);
|
||||
let wide_neg = r_neg != range_neg;
|
||||
let wide_uns = if wide_neg {
|
||||
// 0x0000 ≤ add ≤ 0x00ff
|
||||
let add = ($uu1 << frac_bits) - 1;
|
||||
// If frac_bits is 0: add = 0x0000; 0x0000 ≤ shifted ≤ 0x7f80
|
||||
// If frac_bits is max: add = 0x00ff; 0x0000 ≤ shifted ≤ 0x0080
|
||||
let shifted = (wide_abs + add) >> frac_bits;
|
||||
shifted.wrapping_neg()
|
||||
} else {
|
||||
wide_abs >> frac_bits
|
||||
};
|
||||
let wide = wide_uns as $ii;
|
||||
let (wide_ret, overflow1) = wide.overflowing_add(start as $ii);
|
||||
let ret = wide_ret as $i;
|
||||
let overflow2 = wide_ret != ret as $ii;
|
||||
(ret, overflow1 | overflow2)
|
||||
}
|
||||
|
||||
pub const fn $u(r: $u, start: $u, end: $u, frac_bits: u32) -> ($u, bool) {
|
||||
// 0x00 ≤ range_abs ≤ 0xff
|
||||
let (range_abs, range_neg) = if end >= start {
|
||||
(end - start, false)
|
||||
} else {
|
||||
(start - end, true)
|
||||
};
|
||||
// 0x0000 ≤ wide_abs ≤ 0xfe01
|
||||
let wide_abs = (r as $uu) * (range_abs as $uu);
|
||||
let (wide_ret, overflow1) = if range_neg {
|
||||
// 0x0000 ≤ add ≤ 0x00ff
|
||||
let add = ($uu1 << frac_bits) - 1;
|
||||
// If frac_bits is 0: add = 0x0000; 0x0000 ≤ shifted ≤ 0xfe01
|
||||
// If frac_bits is max: add = 0x00ff; 0x0000 ≤ shifted ≤ 0x00ff
|
||||
let shifted = (wide_abs + add) >> frac_bits;
|
||||
(start as $uu).overflowing_sub(shifted)
|
||||
} else {
|
||||
let shifted = wide_abs >> frac_bits;
|
||||
(start as $uu).overflowing_add(shifted)
|
||||
};
|
||||
let ret = wide_ret as $u;
|
||||
let overflow2 = wide_ret != ret as $uu;
|
||||
(ret, overflow1 | overflow2)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
make_lerp! { i8, u8, i16, u16, 1u16 }
|
||||
make_lerp! { i16, u16, i32, u32, 1u32 }
|
||||
make_lerp! { i32, u32, i64, u64, 1u64 }
|
||||
make_lerp! { i64, u64, i128, u128, 1u128 }
|
||||
|
||||
pub const fn i128(r: i128, start: i128, end: i128, frac_bits: u32) -> (i128, bool) {
|
||||
// 0x00 ≤ r_abs ≤ 0x80
|
||||
let (r_abs, r_neg) = if r >= 0 {
|
||||
(r as u128, false)
|
||||
} else {
|
||||
(r.wrapping_neg() as u128, true)
|
||||
};
|
||||
// 0x00 ≤ range_abs ≤ 0xff
|
||||
let (range_abs, range_neg) = if end >= start {
|
||||
(end.wrapping_sub(start) as u128, false)
|
||||
} else {
|
||||
(start.wrapping_sub(end) as u128, true)
|
||||
};
|
||||
// 0x0000 ≤ wide_abs ≤ 0x7f80
|
||||
let wide_abs = int256::wide_mul_u128(r_abs, range_abs);
|
||||
let wide_neg = r_neg != range_neg;
|
||||
let wide_uns = if wide_neg {
|
||||
// 0x0000 ≤ add ≤ 0x00ff
|
||||
let add = if frac_bits == 128 {
|
||||
u128::MAX
|
||||
} else {
|
||||
(1u128 << frac_bits) - 1
|
||||
};
|
||||
// If frac_bits is 0: add = 0x0000; 0x0000 ≤ shifted ≤ 0x7f80
|
||||
// If frac_bits is max: add = 0x00ff; 0x0000 ≤ shifted ≤ 0x0080
|
||||
let sum = int256::wrapping_add_u256_u128(wide_abs, add);
|
||||
let shifted = int256::shl_u256_max_128(sum, frac_bits);
|
||||
int256::wrapping_neg_u256(shifted)
|
||||
} else {
|
||||
int256::shl_u256_max_128(wide_abs, frac_bits)
|
||||
};
|
||||
let wide = int256::u256_wrapping_as_i256(wide_uns);
|
||||
let (wide_ret, overflow1) = int256::overflowing_add_i256_i128(wide, start);
|
||||
let ret = wide_ret.lo as i128;
|
||||
let overflow2 = if ret < 0 {
|
||||
wide_ret.hi != -1
|
||||
} else {
|
||||
wide_ret.hi != 0
|
||||
};
|
||||
(ret, overflow1 | overflow2)
|
||||
}
|
||||
|
||||
pub const fn u128(r: u128, start: u128, end: u128, frac_bits: u32) -> (u128, bool) {
|
||||
// 0x00 ≤ range_abs ≤ 0xff
|
||||
let (range_abs, range_neg) = if end >= start {
|
||||
(end - start, false)
|
||||
} else {
|
||||
(start - end, true)
|
||||
};
|
||||
// 0x0000 ≤ wide_abs ≤ 0xfe01
|
||||
let wide_abs = int256::wide_mul_u128(r, range_abs);
|
||||
if range_neg {
|
||||
// 0x0000 ≤ add ≤ 0x00ff
|
||||
let add = if frac_bits == 128 {
|
||||
u128::MAX
|
||||
} else {
|
||||
(1u128 << frac_bits) - 1
|
||||
};
|
||||
// If frac_bits is 0: add = 0x0000; 0x0000 ≤ shifted ≤ 0xfe01
|
||||
// If frac_bits is max: add = 0x00ff; 0x0000 ≤ shifted ≤ 0x00ff
|
||||
let sum = int256::wrapping_add_u256_u128(wide_abs, add);
|
||||
let shifted = int256::shl_u256_max_128(sum, frac_bits);
|
||||
int256::overflowing_sub_u128_u256(start, shifted)
|
||||
} else {
|
||||
let shifted = int256::shl_u256_max_128(wide_abs, frac_bits);
|
||||
int256::overflowing_add_u128_u256(start, shifted)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::lerp;
|
||||
|
||||
#[test]
|
||||
fn lerp_i8() {
|
||||
assert_eq!(lerp::i8(0, -128, 127, 0), (-128, false));
|
||||
assert_eq!(lerp::i8(0, 127, -128, 8), (127, false));
|
||||
assert_eq!(lerp::i8(1, -128, 127, 1), (-1, false));
|
||||
assert_eq!(lerp::i8(-64, -100, -110, 6), (-90, false));
|
||||
|
||||
assert_eq!(lerp::i8(2 << 2, 0, 50, 2), (100, false));
|
||||
assert_eq!(lerp::i8(2 << 2, 0, 100, 2), (-56, true));
|
||||
assert_eq!(lerp::i8((-1) << 2, 50, 0, 2), (100, false));
|
||||
assert_eq!(lerp::i8((-1) << 2, 100, 0, 2), (-56, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lerp_u8() {
|
||||
assert_eq!(lerp::u8(0, 0, 255, 0), (0, false));
|
||||
assert_eq!(lerp::u8(0, 255, 0, 8), (255, false));
|
||||
assert_eq!(lerp::u8(1, 0, 255, 0), (255, false));
|
||||
assert_eq!(lerp::u8(128, 255, 0, 7), (0, false));
|
||||
|
||||
assert_eq!(lerp::u8(2 << 2, 0, 100, 2), (200, false));
|
||||
assert_eq!(lerp::u8(2 << 2, 0, 200, 2), (200 - 56, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lerp_i128() {
|
||||
assert_eq!(lerp::i128(0, -128, 127, 0), (-128, false));
|
||||
assert_eq!(lerp::i128(0, 127, -128, 128), (127, false));
|
||||
assert_eq!(lerp::i128(1, -128, 127, 1), (-1, false));
|
||||
assert_eq!(lerp::i128(1 << 98, 127, -128, 100), (63, false));
|
||||
assert_eq!(lerp::i128((-64) << 120, -100, -110, 126), (-90, false));
|
||||
|
||||
assert_eq!(
|
||||
lerp::i128(2 << 2, 0, i128::MAX / 2, 2),
|
||||
(i128::MAX / 2 * 2, false)
|
||||
);
|
||||
assert_eq!(
|
||||
lerp::i128(2 << 2, 0, i128::MAX, 2),
|
||||
(i128::MAX.wrapping_mul(2), true)
|
||||
);
|
||||
assert_eq!(
|
||||
lerp::i128((-1) << 2, i128::MAX / 2, 0, 2),
|
||||
(i128::MAX / 2 * 2, false)
|
||||
);
|
||||
assert_eq!(
|
||||
lerp::i128((-1) << 2, i128::MAX, 0, 2),
|
||||
(i128::MAX.wrapping_mul(2), true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lerp_u128() {
|
||||
assert_eq!(lerp::u128(0, 0, 255, 0), (0, false));
|
||||
assert_eq!(lerp::u128(0, 255, 0, 128), (255, false));
|
||||
assert_eq!(lerp::u128(1, 0, 255, 0), (255, false));
|
||||
assert_eq!(lerp::u128(128 << 120, 255, 0, 127), (0, false));
|
||||
|
||||
assert_eq!(
|
||||
lerp::u128(2 << 2, 0, u128::MAX / 2, 2),
|
||||
(u128::MAX / 2 * 2, false)
|
||||
);
|
||||
assert_eq!(
|
||||
lerp::u128(2 << 2, 0, u128::MAX, 2),
|
||||
(u128::MAX.wrapping_mul(2), true)
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+1635
File diff suppressed because it is too large.
Load diff
Vendored
+178
@@ -0,0 +1,178 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Base(u32);
|
||||
|
||||
impl Base {
|
||||
pub const fn new(base: u32) -> Option<Base> {
|
||||
if base >= 2 { Some(Base(base)) } else { None }
|
||||
}
|
||||
|
||||
pub const fn get(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_int_part {
|
||||
($u:ident) => {
|
||||
pub const fn $u(val: NonZero<$u>, base: Base) -> i32 {
|
||||
const MAX_TABLE_SIZE: usize = ($u::BITS.ilog2() - 1) as usize;
|
||||
|
||||
let val = val.get();
|
||||
let base = base.get();
|
||||
|
||||
let baseu = base as $u;
|
||||
if baseu as u32 != base || val < baseu {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// base^1, base^2, base^4, etc.
|
||||
let mut base_powers: [$u; MAX_TABLE_SIZE] = [0; MAX_TABLE_SIZE];
|
||||
|
||||
let mut i = 0;
|
||||
let mut partial_log = 1u32;
|
||||
let mut partial_val = baseu;
|
||||
|
||||
loop {
|
||||
let square = match partial_val.checked_mul(partial_val) {
|
||||
Some(s) if val >= s => s,
|
||||
_ => break,
|
||||
};
|
||||
base_powers[i] = partial_val;
|
||||
i += 1;
|
||||
partial_log *= 2;
|
||||
partial_val = square;
|
||||
}
|
||||
let mut dlog = partial_log;
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
dlog /= 2;
|
||||
if let Some(mid) = partial_val.checked_mul(base_powers[i]) {
|
||||
if val >= mid {
|
||||
partial_val = mid;
|
||||
partial_log += dlog;
|
||||
}
|
||||
}
|
||||
}
|
||||
return partial_log as i32;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod int_part {
|
||||
use crate::log::Base;
|
||||
use core::num::NonZero;
|
||||
|
||||
impl_int_part! { u8 }
|
||||
impl_int_part! { u16 }
|
||||
impl_int_part! { u32 }
|
||||
impl_int_part! { u64 }
|
||||
impl_int_part! { u128 }
|
||||
}
|
||||
|
||||
macro_rules! impl_frac_part {
|
||||
($u:ident) => {
|
||||
pub const fn $u(val: NonZero<$u>, base: Base) -> i32 {
|
||||
const MAX_TABLE_SIZE: usize = ($u::BITS.ilog2() - 1) as usize;
|
||||
|
||||
let val = val.get();
|
||||
let base = base.get();
|
||||
|
||||
let baseu = base as $u;
|
||||
if baseu as u32 != base || val.checked_mul(baseu).is_none() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// base^1, base^2, base^4, etc.
|
||||
let mut base_powers: [$u; MAX_TABLE_SIZE] = [0; MAX_TABLE_SIZE];
|
||||
|
||||
let mut i = 0;
|
||||
let mut partial_log = 1u32;
|
||||
let mut partial_val = baseu;
|
||||
|
||||
loop {
|
||||
let square = match partial_val.checked_mul(partial_val) {
|
||||
Some(s) if val.checked_mul(s).is_some() => s,
|
||||
_ => break,
|
||||
};
|
||||
base_powers[i] = partial_val;
|
||||
i += 1;
|
||||
partial_log *= 2;
|
||||
partial_val = square;
|
||||
}
|
||||
let mut dlog = partial_log;
|
||||
while i > 0 {
|
||||
i -= 1;
|
||||
dlog /= 2;
|
||||
if let Some(mid) = partial_val.checked_mul(base_powers[i]) {
|
||||
if val.checked_mul(mid).is_some() {
|
||||
partial_val = mid;
|
||||
partial_log += dlog;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1 - partial_log as i32;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod frac_part {
|
||||
use crate::log::Base;
|
||||
use core::num::NonZero;
|
||||
|
||||
impl_frac_part! { u8 }
|
||||
impl_frac_part! { u16 }
|
||||
impl_frac_part! { u32 }
|
||||
impl_frac_part! { u64 }
|
||||
impl_frac_part! { u128 }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::log;
|
||||
use crate::log::Base;
|
||||
use core::num::NonZero;
|
||||
|
||||
// these tests require the maximum table sizes
|
||||
#[test]
|
||||
fn check_table_size_is_sufficient() {
|
||||
let bin = Base::new(2).unwrap();
|
||||
|
||||
assert_eq!(log::int_part::u8(NonZero::<u8>::MAX, bin), 7);
|
||||
assert_eq!(log::int_part::u16(NonZero::<u16>::MAX, bin), 15);
|
||||
assert_eq!(log::int_part::u32(NonZero::<u32>::MAX, bin), 31);
|
||||
assert_eq!(log::int_part::u64(NonZero::<u64>::MAX, bin), 63);
|
||||
assert_eq!(log::int_part::u128(NonZero::<u128>::MAX, bin), 127);
|
||||
|
||||
assert_eq!(log::frac_part::u8(NonZero::<u8>::new(1).unwrap(), bin), -8);
|
||||
assert_eq!(
|
||||
log::frac_part::u16(NonZero::<u16>::new(1).unwrap(), bin),
|
||||
-16
|
||||
);
|
||||
assert_eq!(
|
||||
log::frac_part::u32(NonZero::<u32>::new(1).unwrap(), bin),
|
||||
-32
|
||||
);
|
||||
assert_eq!(
|
||||
log::frac_part::u64(NonZero::<u64>::new(1).unwrap(), bin),
|
||||
-64
|
||||
);
|
||||
assert_eq!(
|
||||
log::frac_part::u128(NonZero::<u128>::new(1).unwrap(), bin),
|
||||
-128
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+325
@@ -0,0 +1,325 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
// the value must be positive for all public functions
|
||||
|
||||
pub mod frac_part {
|
||||
use core::num::NonZero;
|
||||
|
||||
// MAX / 1000 (0) < val <= MAX (255)
|
||||
// -3 <= log <= -1
|
||||
#[inline]
|
||||
pub const fn u8(val: NonZero<u8>) -> i32 {
|
||||
let val = val.get();
|
||||
if val > 25 {
|
||||
-1
|
||||
} else if val > 2 {
|
||||
-2
|
||||
} else {
|
||||
-3
|
||||
}
|
||||
}
|
||||
|
||||
// MAX / 100_000 (0) < val <= MAX (65_535)
|
||||
// -5 <= log <= -1
|
||||
#[inline]
|
||||
pub const fn u16(val: NonZero<u16>) -> i32 {
|
||||
let val = val.get();
|
||||
if val > 6553 {
|
||||
-1
|
||||
} else if val > 655 {
|
||||
-2
|
||||
} else if val > 65 {
|
||||
-3
|
||||
} else if val > 6 {
|
||||
-4
|
||||
} else {
|
||||
-5
|
||||
}
|
||||
}
|
||||
|
||||
// 0 < val <= MAX
|
||||
// -10 <= log <= -1
|
||||
pub const fn u32(val: NonZero<u32>) -> i32 {
|
||||
const MAX: u32 = u32::MAX;
|
||||
let mut val = val.get();
|
||||
if val <= MAX / 100_000_000 {
|
||||
val *= 100_000_000;
|
||||
// At this point, we have shifted out 8 digits, and we can only shift out 2 more.
|
||||
// We can only check up to -2 more because -10 <= log <= -8.
|
||||
if val > MAX / 10 {
|
||||
-9
|
||||
} else {
|
||||
debug_assert!(val > MAX / 100);
|
||||
-10
|
||||
}
|
||||
} else {
|
||||
greater_equal_m8_u32(val)
|
||||
}
|
||||
}
|
||||
|
||||
// 0 < val <= MAX
|
||||
// -20 <= log <= -1
|
||||
pub const fn u64(val: NonZero<u64>) -> i32 {
|
||||
const MAX: u64 = u64::MAX;
|
||||
let mut val = val.get();
|
||||
let mut log = 0;
|
||||
if val <= MAX / 10_000_000_000_000_000 {
|
||||
// After this, we can only check up to -4 more because -20 <= log <= -16.
|
||||
// That is, we can skip the checks against MAX / 100_000_000 and MAX / 10_000.
|
||||
val *= 10_000_000_000_000_000;
|
||||
log += -16;
|
||||
} else {
|
||||
if val <= MAX / 100_000_000 {
|
||||
val *= 100_000_000;
|
||||
log += -8;
|
||||
}
|
||||
if val <= MAX / 10_000 {
|
||||
val *= 10_000;
|
||||
log += -4;
|
||||
}
|
||||
}
|
||||
log + if val > MAX / 10 {
|
||||
-1
|
||||
} else if val > MAX / 100 {
|
||||
-2
|
||||
} else if val > MAX / 1000 {
|
||||
-3
|
||||
} else {
|
||||
debug_assert!(val > MAX / 10_000);
|
||||
-4
|
||||
}
|
||||
}
|
||||
|
||||
// 0 < val <= MAX
|
||||
// -39 <= log <= -1
|
||||
pub const fn u128(val: NonZero<u128>) -> i32 {
|
||||
const MAX: u128 = u128::MAX;
|
||||
let mut val = val.get();
|
||||
let mut log = 0;
|
||||
if val <= MAX / 100_000_000_000_000_000_000_000_000_000_000 {
|
||||
val *= 100_000_000_000_000_000_000_000_000_000_000;
|
||||
// At this point we have shifted out 32 digits, and we can only shift out 7 more.
|
||||
// We can
|
||||
// * use val >> 96 because we have shifted out 32 decimal digits (106 bits)
|
||||
// * only check up to -8 more because -39 <= log <= -32
|
||||
return -32 + greater_equal_m8_u32((val >> 96) as u32);
|
||||
}
|
||||
if val <= MAX / 10_000_000_000_000_000 {
|
||||
val *= 10_000_000_000_000_000;
|
||||
log += -16;
|
||||
}
|
||||
if val <= MAX / 100_000_000 {
|
||||
val *= 100_000_000;
|
||||
log += -8;
|
||||
}
|
||||
if log == -24 {
|
||||
// At this point we have shifted out 24 digits, and we can only shift out 15 more.
|
||||
// We can
|
||||
// * use val >> 64 because we have shifted out 24 decimal digits (79 bits)
|
||||
// * only check up to -8 more because -32 <= log <= -24
|
||||
return -24 + greater_equal_m8_u64((val >> 64) as u64);
|
||||
}
|
||||
// We have *not* shifted out enough decimal digits, so we must *not* convert to u32 or u64.
|
||||
if val <= MAX / 10_000 {
|
||||
val *= 10_000;
|
||||
log += -4;
|
||||
}
|
||||
log + if val > MAX / 10 {
|
||||
-1
|
||||
} else if val > MAX / 100 {
|
||||
-2
|
||||
} else if val > MAX / 1000 {
|
||||
-3
|
||||
} else {
|
||||
debug_assert!(val > MAX / 10_000);
|
||||
-4
|
||||
}
|
||||
}
|
||||
|
||||
// MAX / 100_000_000 < val <= MAX
|
||||
// -8 <= log <= -1
|
||||
const fn greater_equal_m8_u32(mut val: u32) -> i32 {
|
||||
const MAX: u32 = u32::MAX;
|
||||
debug_assert!(val > MAX / 100_000_000);
|
||||
let mut log = 0;
|
||||
if val <= MAX / 10_000 {
|
||||
val *= 10_000;
|
||||
log += -4;
|
||||
}
|
||||
log + if val > MAX / 10 {
|
||||
-1
|
||||
} else if val > MAX / 100 {
|
||||
-2
|
||||
} else if val > MAX / 1000 {
|
||||
-3
|
||||
} else {
|
||||
debug_assert!(val > MAX / 10_000);
|
||||
-4
|
||||
}
|
||||
}
|
||||
|
||||
// MAX / 100_000_000 < val <= MAX
|
||||
// -8 <= log <= 1
|
||||
const fn greater_equal_m8_u64(mut val: u64) -> i32 {
|
||||
const MAX: u64 = u64::MAX;
|
||||
debug_assert!(val > MAX / 100_000_000);
|
||||
let mut log = 0;
|
||||
if val <= MAX / 10_000 {
|
||||
val *= 10_000;
|
||||
log += -4;
|
||||
}
|
||||
log + if val > MAX / 10 {
|
||||
-1
|
||||
} else if val > MAX / 100 {
|
||||
-2
|
||||
} else if val > MAX / 1000 {
|
||||
-3
|
||||
} else {
|
||||
debug_assert!(val > MAX / 10_000);
|
||||
-4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check log10() and log(10) in tandem
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::log;
|
||||
use crate::log::Base;
|
||||
use crate::log10;
|
||||
use core::num::NonZero;
|
||||
|
||||
const DEC: Base = match Base::new(10) {
|
||||
Some(s) => s,
|
||||
None => unreachable!(),
|
||||
};
|
||||
|
||||
macro_rules! check_loop {
|
||||
($T:ident) => {
|
||||
for i in 0..=$T::MAX.ilog10() as i32 {
|
||||
let p = (10 as $T).pow(i as u32);
|
||||
let nz = NonZero::<$T>::new(p).unwrap();
|
||||
if i > 0 {
|
||||
let nz_m1 = NonZero::<$T>::new(p - 1).unwrap();
|
||||
assert_eq!((p - 1).ilog10() as i32, i - 1);
|
||||
assert_eq!(log::int_part::$T(nz_m1, DEC), i - 1);
|
||||
}
|
||||
assert_eq!(p.ilog10() as i32, i);
|
||||
assert_eq!(log::int_part::$T(nz, DEC), i);
|
||||
let nz_p1 = NonZero::<$T>::new(p + 1).unwrap();
|
||||
assert_eq!((p + 1).ilog10() as i32, i);
|
||||
assert_eq!(log::int_part::$T(nz_p1, DEC), i);
|
||||
}
|
||||
|
||||
for i in 0..-log10::frac_part::$T(NonZero::<$T>::new(1).unwrap()) {
|
||||
let p = <$T>::MAX / (10 as $T).pow(i as u32);
|
||||
let nz = NonZero::<$T>::new(p).unwrap();
|
||||
if p > 1 {
|
||||
let nz_m1 = NonZero::<$T>::new(p - 1).unwrap();
|
||||
assert_eq!(log10::frac_part::$T(nz_m1), -1 - i);
|
||||
assert_eq!(log::frac_part::$T(nz_m1, DEC), -1 - i);
|
||||
}
|
||||
assert_eq!(log10::frac_part::$T(nz), -1 - i);
|
||||
assert_eq!(log::frac_part::$T(nz, DEC), -1 - i);
|
||||
if i > 0 {
|
||||
let nz_p1 = NonZero::<$T>::new(p + 1).unwrap();
|
||||
assert_eq!(log10::frac_part::$T(nz_p1), -i);
|
||||
assert_eq!(log::frac_part::$T(nz_p1, DEC), -i);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn log10_u8() {
|
||||
let one = NonZero::<u8>::new(1).unwrap();
|
||||
|
||||
assert_eq!(1u8.ilog10(), 0);
|
||||
assert_eq!(log::int_part::u8(one, DEC), 0);
|
||||
assert_eq!(u8::MAX.ilog10(), 2);
|
||||
assert_eq!(log::int_part::u8(NonZero::<u8>::MAX, DEC), 2);
|
||||
assert_eq!(log10::frac_part::u8(one), -3);
|
||||
assert_eq!(log::frac_part::u8(one, DEC), -3);
|
||||
assert_eq!(log10::frac_part::u8(NonZero::<u8>::MAX), -1);
|
||||
assert_eq!(log::frac_part::u8(NonZero::<u8>::MAX, DEC), -1);
|
||||
|
||||
check_loop! { u8 }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn log10_u16() {
|
||||
let one = NonZero::<u16>::new(1).unwrap();
|
||||
|
||||
assert_eq!(1u16.ilog10(), 0);
|
||||
assert_eq!(log::int_part::u16(one, DEC), 0);
|
||||
assert_eq!(u16::MAX.ilog10(), 4);
|
||||
assert_eq!(log::int_part::u16(NonZero::<u16>::MAX, DEC), 4);
|
||||
assert_eq!(log10::frac_part::u16(one), -5);
|
||||
assert_eq!(log::frac_part::u16(one, DEC), -5);
|
||||
assert_eq!(log10::frac_part::u16(NonZero::<u16>::MAX), -1);
|
||||
assert_eq!(log::frac_part::u16(NonZero::<u16>::MAX, DEC), -1);
|
||||
|
||||
check_loop! { u16 }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn log10_u32() {
|
||||
let one = NonZero::<u32>::new(1).unwrap();
|
||||
|
||||
assert_eq!(1u32.ilog10(), 0);
|
||||
assert_eq!(log::int_part::u32(NonZero::<u32>::new(1).unwrap(), DEC), 0);
|
||||
assert_eq!(u32::MAX.ilog10(), 9);
|
||||
assert_eq!(log::int_part::u32(NonZero::<u32>::MAX, DEC), 9);
|
||||
assert_eq!(log10::frac_part::u32(one), -10);
|
||||
assert_eq!(log::frac_part::u32(one, DEC), -10);
|
||||
assert_eq!(log10::frac_part::u32(NonZero::<u32>::MAX), -1);
|
||||
assert_eq!(log::frac_part::u32(NonZero::<u32>::MAX, DEC), -1);
|
||||
|
||||
check_loop! { u32 }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn log10_u64() {
|
||||
let one = NonZero::<u64>::new(1).unwrap();
|
||||
|
||||
assert_eq!(1u64.ilog10(), 0);
|
||||
assert_eq!(log::int_part::u64(one, DEC), 0);
|
||||
assert_eq!(u64::MAX.ilog10(), 19);
|
||||
assert_eq!(log::int_part::u64(NonZero::<u64>::MAX, DEC), 19);
|
||||
assert_eq!(log10::frac_part::u64(one), -20);
|
||||
assert_eq!(log::frac_part::u64(one, DEC), -20);
|
||||
assert_eq!(log10::frac_part::u64(NonZero::<u64>::MAX), -1);
|
||||
assert_eq!(log::frac_part::u64(NonZero::<u64>::MAX, DEC), -1);
|
||||
|
||||
check_loop! { u64 }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn log10_u128() {
|
||||
let one = NonZero::<u128>::new(1).unwrap();
|
||||
|
||||
assert_eq!(1u128.ilog10(), 0);
|
||||
assert_eq!(log::int_part::u128(one, DEC), 0);
|
||||
assert_eq!(u128::MAX.ilog10(), 38);
|
||||
assert_eq!(log::int_part::u128(NonZero::<u128>::MAX, DEC), 38);
|
||||
assert_eq!(log10::frac_part::u128(one), -39);
|
||||
assert_eq!(log::frac_part::u128(one, DEC), -39);
|
||||
assert_eq!(log10::frac_part::u128(NonZero::<u128>::MAX), -1);
|
||||
assert_eq!(log::frac_part::u128(NonZero::<u128>::MAX, DEC), -1);
|
||||
|
||||
check_loop! { u128 }
|
||||
}
|
||||
}
|
||||
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
macro_rules! if_signed {
|
||||
(Signed; $($rem:tt)+) => {
|
||||
$($rem)+
|
||||
};
|
||||
(Unsigned; $($rem:tt)+) => {};
|
||||
}
|
||||
|
||||
macro_rules! if_unsigned {
|
||||
(Signed; $($rem:tt)+) => {};
|
||||
(Unsigned; $($rem:tt)+) => {
|
||||
$($rem)+
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! if_signed_unsigned {
|
||||
(Signed, $signed:expr, $unsigned:expr $(,)?) => {
|
||||
$signed
|
||||
};
|
||||
(Unsigned, $signed:expr, $unsigned:expr $(,)?) => {
|
||||
$unsigned
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! if_signed_else_empty_str {
|
||||
(Signed; $($signed:tt)*) => {
|
||||
concat!($($signed)*)
|
||||
};
|
||||
(Unsigned; $($signed:tt)*) => {
|
||||
""
|
||||
};
|
||||
}
|
||||
macro_rules! if_unsigned_else_empty_str {
|
||||
(Signed; $($unsigned:tt)*) => {
|
||||
""
|
||||
};
|
||||
(Unsigned; $($unsigned:tt)*) => {
|
||||
concat!($($unsigned)*)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! doc_comment {
|
||||
($comment:expr; $($tt:tt)*) => {
|
||||
#[doc = $comment]
|
||||
$($tt)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! comment {
|
||||
($($comment:expr),* $(,)?; $($tt:tt)*) => {
|
||||
doc_comment! {
|
||||
concat!($($comment),*);
|
||||
$($tt)*
|
||||
}
|
||||
};
|
||||
}
|
||||
Vendored
+413
@@ -0,0 +1,413 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
macro_rules! fixed_const {
|
||||
(
|
||||
Self = $Self:ident,
|
||||
Signedness = $Signedness:ident,
|
||||
LeEqU = $LeEqU:ident,
|
||||
[nm4 ..= n] = [$nm4:literal, $nm3:literal, $nm2:literal, $nm1:literal, $n:literal],
|
||||
[LeEqUC0 ..= LeEqUC3] = [$LeEqUC0:ident, $LeEqUC1:ident, $LeEqUC2:ident, $LeEqUC3:ident],
|
||||
) => {
|
||||
impl<Frac: Unsigned> $Self<Frac> {
|
||||
const fn from_const<SrcFrac: Unsigned>(src: FixedU128<SrcFrac>) -> $Self<Frac> {
|
||||
let right = SrcFrac::U32 - Frac::U32;
|
||||
let bits128 = if right == 128 {
|
||||
0
|
||||
} else {
|
||||
src.to_bits() >> right
|
||||
};
|
||||
$Self::from_bits(bits128 as _)
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"This block contains constants in the range 0 < <i>x</i> < 0.5.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::consts;
|
||||
use fixed::types::extra::U", $n, ";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U", $n, ">;
|
||||
assert_eq!(Fix::LOG10_2, Fix::from_num(consts::LOG10_2));
|
||||
```
|
||||
";
|
||||
impl<Frac: $LeEqU> $Self<Frac> {
|
||||
/// 1/τ = 0.159154…
|
||||
pub const FRAC_1_TAU: $Self<Frac> = Self::from_const(consts::FRAC_1_TAU);
|
||||
|
||||
/// 2/τ = 0.318309…
|
||||
pub const FRAC_2_TAU: $Self<Frac> = Self::from_const(consts::FRAC_2_TAU);
|
||||
|
||||
/// π/8 = 0.392699…
|
||||
pub const FRAC_PI_8: $Self<Frac> = Self::from_const(consts::FRAC_PI_8);
|
||||
|
||||
/// 1/π = 0.318309…
|
||||
pub const FRAC_1_PI: $Self<Frac> = Self::from_const(consts::FRAC_1_PI);
|
||||
|
||||
/// 1/√2π = 0.398942…
|
||||
pub const FRAC_1_SQRT_2PI: $Self<Frac> = Self::from_const(consts::FRAC_1_SQRT_2PI);
|
||||
|
||||
/// log<sub>10</sub> 2 = 0.301029…
|
||||
pub const LOG10_2: $Self<Frac> = Self::from_const(consts::LOG10_2);
|
||||
|
||||
/// log<sub>10</sub> e = 0.434294…
|
||||
pub const LOG10_E: $Self<Frac> = Self::from_const(consts::LOG10_E);
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"This block contains constants in the range 0.5 ≤ <i>x</i> < 1",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness;
|
||||
", and −1.
|
||||
|
||||
These constants are not representable in signed fixed-point numbers with less
|
||||
than 1 integer bit"
|
||||
},
|
||||
".
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::consts;
|
||||
use fixed::types::extra::U",
|
||||
if_signed_unsigned!($Signedness, $nm1, $n),
|
||||
";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U",
|
||||
if_signed_unsigned!($Signedness, $nm1, $n),
|
||||
">;
|
||||
assert_eq!(Fix::LN_2, Fix::from_num(consts::LN_2));
|
||||
assert!(0.5 <= Fix::LN_2 && Fix::LN_2 < 1);
|
||||
```
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness;
|
||||
"
|
||||
The following example fails to compile, since the maximum
|
||||
representable value with ", $n, " fractional bits and 0 integer
|
||||
bits is < 0.5.
|
||||
|
||||
```rust,compile_fail
|
||||
use fixed::consts;
|
||||
use fixed::types::extra::U", $n, ";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U", $n, ">;
|
||||
let _ = Fix::LN_2;
|
||||
```
|
||||
"
|
||||
};
|
||||
impl<Frac: Unsigned> $Self<Frac>
|
||||
where
|
||||
Frac: IsLessOrEqual<$LeEqUC0, Output = True>,
|
||||
{
|
||||
if_signed! {
|
||||
$Signedness;
|
||||
comment! {
|
||||
"Negative one.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::types::extra::U", $nm1, ";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U", $nm1, ">;
|
||||
assert_eq!(Fix::NEG_ONE, Fix::from_num(-1));
|
||||
```
|
||||
|
||||
The following would fail as
|
||||
<code>[", stringify!($Self), "]<[U", $nm1, "]></code>
|
||||
cannot represent 1, so there is no
|
||||
<code>[", stringify!($Self), "]::<[U", $nm1, "]>::[ONE]</code>.
|
||||
|
||||
[ONE]: ", stringify!($Self), "::ONE
|
||||
|
||||
```rust,compile_fail
|
||||
use fixed::types::extra::U", $nm1, ";
|
||||
use fixed::", stringify!($Self), ";
|
||||
const _ERROR: ", stringify!($Self), "<U", $nm1, "> = ", stringify!($Self), "::ONE.unwrapped_neg();
|
||||
```
|
||||
";
|
||||
pub const NEG_ONE: $Self<Frac> = Self::from_bits(-1 << Frac::U32);
|
||||
}
|
||||
}
|
||||
|
||||
/// τ/8 = 0.785398…
|
||||
pub const FRAC_TAU_8: $Self<Frac> = Self::from_const(consts::FRAC_TAU_8);
|
||||
|
||||
/// τ/12 = 0.523598…
|
||||
pub const FRAC_TAU_12: $Self<Frac> = Self::from_const(consts::FRAC_TAU_12);
|
||||
|
||||
/// 4/τ = 0.636619…
|
||||
pub const FRAC_4_TAU: $Self<Frac> = Self::from_const(consts::FRAC_4_TAU);
|
||||
|
||||
/// π/4 = 0.785398…
|
||||
pub const FRAC_PI_4: $Self<Frac> = Self::from_const(consts::FRAC_PI_4);
|
||||
|
||||
/// π/6 = 0.523598…
|
||||
pub const FRAC_PI_6: $Self<Frac> = Self::from_const(consts::FRAC_PI_6);
|
||||
|
||||
/// 2/π = 0.636619…
|
||||
pub const FRAC_2_PI: $Self<Frac> = Self::from_const(consts::FRAC_2_PI);
|
||||
|
||||
/// 1/√π = 0.564189…
|
||||
pub const FRAC_1_SQRT_PI: $Self<Frac> = Self::from_const(consts::FRAC_1_SQRT_PI);
|
||||
|
||||
/// 1/√2 = 0.707106…
|
||||
pub const FRAC_1_SQRT_2: $Self<Frac> = Self::from_const(consts::FRAC_1_SQRT_2);
|
||||
|
||||
/// 1/√3 = 0.577350…
|
||||
pub const FRAC_1_SQRT_3: $Self<Frac> = Self::from_const(consts::FRAC_1_SQRT_3);
|
||||
|
||||
/// ln 2 = 0.693147…
|
||||
pub const LN_2: $Self<Frac> = Self::from_const(consts::LN_2);
|
||||
|
||||
/// The golden ratio conjugate, Φ = 1/φ = 0.618033…
|
||||
pub const FRAC_1_PHI: $Self<Frac> = Self::from_const(consts::FRAC_1_PHI);
|
||||
|
||||
/// The Euler-Mascheroni constant, γ = 0.577215…
|
||||
pub const GAMMA: $Self<Frac> = Self::from_const(consts::GAMMA);
|
||||
|
||||
/// Catalan’s constant = 0.915965…
|
||||
pub const CATALAN: $Self<Frac> = Self::from_const(consts::CATALAN);
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"This block contains constants in the range 1 ≤ <i>x</i> < 2.
|
||||
|
||||
These constants are not representable in ",
|
||||
if_signed_unsigned!($Signedness, "signed", "unsigned"),
|
||||
" fixed-point numbers with less than ",
|
||||
if_signed_unsigned!($Signedness, "2 integer bits", "1 integer bit"),
|
||||
".
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::consts;
|
||||
use fixed::types::extra::U",
|
||||
if_signed_unsigned!($Signedness, $nm2, $nm1),
|
||||
";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U",
|
||||
if_signed_unsigned!($Signedness, $nm2, $nm1),
|
||||
">;
|
||||
assert_eq!(Fix::LOG2_E, Fix::from_num(consts::LOG2_E));
|
||||
assert!(1 <= Fix::LOG2_E && Fix::LOG2_E < 2);
|
||||
```
|
||||
|
||||
The following example fails to compile, since the maximum
|
||||
representable value with ",
|
||||
if_signed_unsigned!($Signedness, $nm1, $n),
|
||||
" fractional bits and ",
|
||||
if_signed_unsigned!($Signedness, "1 integer bit", "0 integer bits"),
|
||||
" is < 1.
|
||||
|
||||
```rust,compile_fail
|
||||
use fixed::consts;
|
||||
use fixed::types::extra::U",
|
||||
if_signed_unsigned!($Signedness, $nm1, $n),
|
||||
";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U",
|
||||
if_signed_unsigned!($Signedness, $nm1, $n),
|
||||
">;
|
||||
let _ = Fix::LOG2_E;
|
||||
```
|
||||
";
|
||||
impl<Frac: Unsigned> $Self<Frac>
|
||||
where
|
||||
Frac: IsLessOrEqual<$LeEqUC1, Output = True>,
|
||||
{
|
||||
comment! {
|
||||
"One.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::types::extra::U4;
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U4>;
|
||||
assert_eq!(Fix::ONE, Fix::from_num(1));
|
||||
```
|
||||
";
|
||||
pub const ONE: $Self<Frac> = Self::from_bits(1 << Frac::U32);
|
||||
}
|
||||
|
||||
/// τ/4 = 1.57079…
|
||||
pub const FRAC_TAU_4: $Self<Frac> = Self::from_const(consts::FRAC_TAU_4);
|
||||
|
||||
/// τ/6 = 1.04719…
|
||||
pub const FRAC_TAU_6: $Self<Frac> = Self::from_const(consts::FRAC_TAU_6);
|
||||
|
||||
/// π/2 = 1.57079…
|
||||
pub const FRAC_PI_2: $Self<Frac> = Self::from_const(consts::FRAC_PI_2);
|
||||
|
||||
/// π/3 = 1.04719…
|
||||
pub const FRAC_PI_3: $Self<Frac> = Self::from_const(consts::FRAC_PI_3);
|
||||
|
||||
/// √π = 1.77245…
|
||||
pub const SQRT_PI: $Self<Frac> = Self::from_const(consts::SQRT_PI);
|
||||
|
||||
/// 2/√π = 1.12837…
|
||||
pub const FRAC_2_SQRT_PI: $Self<Frac> = Self::from_const(consts::FRAC_2_SQRT_PI);
|
||||
|
||||
/// √2 = 1.41421…
|
||||
pub const SQRT_2: $Self<Frac> = Self::from_const(consts::SQRT_2);
|
||||
|
||||
/// √3 = 1.73205…
|
||||
pub const SQRT_3: $Self<Frac> = Self::from_const(consts::SQRT_3);
|
||||
|
||||
/// √e = 1.64872…
|
||||
pub const SQRT_E: $Self<Frac> = Self::from_const(consts::SQRT_E);
|
||||
|
||||
/// log<sub>2</sub> e = 1.44269…
|
||||
pub const LOG2_E: $Self<Frac> = Self::from_const(consts::LOG2_E);
|
||||
|
||||
/// The golden ratio, φ = 1.61803…
|
||||
pub const PHI: $Self<Frac> = Self::from_const(consts::PHI);
|
||||
|
||||
/// √φ = 1.27201…
|
||||
pub const SQRT_PHI: $Self<Frac> = Self::from_const(consts::SQRT_PHI);
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"This block contains constants in the range 2 ≤ <i>x</i> < 4.
|
||||
|
||||
These constants are not representable in ",
|
||||
if_signed_unsigned!($Signedness, "signed", "unsigned"),
|
||||
" fixed-point numbers with less than ",
|
||||
if_signed_unsigned!($Signedness, 3, 2),
|
||||
" integer bits.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::consts;
|
||||
use fixed::types::extra::U",
|
||||
if_signed_unsigned!($Signedness, $nm3, $nm2),
|
||||
";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U",
|
||||
if_signed_unsigned!($Signedness, $nm3, $nm2),
|
||||
">;
|
||||
assert_eq!(Fix::E, Fix::from_num(consts::E));
|
||||
assert!(2 <= Fix::E && Fix::E < 4);
|
||||
```
|
||||
|
||||
The following example fails to compile, since the maximum
|
||||
representable value with ",
|
||||
if_signed_unsigned!($Signedness, $nm2, $nm1),
|
||||
" fractional bits and ",
|
||||
if_signed_unsigned!($Signedness, "2 integer bits", "1 integer bit"),
|
||||
" is < 2.
|
||||
|
||||
```rust,compile_fail
|
||||
use fixed::consts;
|
||||
use fixed::types::extra::U",
|
||||
if_signed_unsigned!($Signedness, $nm2, $nm1),
|
||||
";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U",
|
||||
if_signed_unsigned!($Signedness, $nm2, $nm1),
|
||||
">;
|
||||
let _ = Fix::E;
|
||||
```
|
||||
";
|
||||
impl<Frac: Unsigned> $Self<Frac>
|
||||
where
|
||||
Frac: IsLessOrEqual<$LeEqUC2, Output = True>,
|
||||
{
|
||||
/// τ/2 = 3.14159…
|
||||
pub const FRAC_TAU_2: $Self<Frac> = Self::from_const(consts::FRAC_TAU_2);
|
||||
|
||||
/// τ/3 = 2.09439…
|
||||
pub const FRAC_TAU_3: $Self<Frac> = Self::from_const(consts::FRAC_TAU_3);
|
||||
|
||||
/// Archimedes’ constant, π = 3.14159…
|
||||
pub const PI: $Self<Frac> = Self::from_const(consts::PI);
|
||||
|
||||
/// √2π = 2.50662…
|
||||
pub const SQRT_2PI: $Self<Frac> = Self::from_const(consts::SQRT_2PI);
|
||||
|
||||
/// Euler’s number, e = 2.71828…
|
||||
pub const E: $Self<Frac> = Self::from_const(consts::E);
|
||||
|
||||
/// log<sub>2</sub> 10 = 3.32192…
|
||||
pub const LOG2_10: $Self<Frac> = Self::from_const(consts::LOG2_10);
|
||||
|
||||
/// ln 10 = 2.30258…
|
||||
pub const LN_10: $Self<Frac> = Self::from_const(consts::LN_10);
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"This block contains constants in the range 4 ≤ <i>x</i> < 8.
|
||||
|
||||
These constants are not representable in ",
|
||||
if_signed_unsigned!($Signedness, "signed", "unsigned"),
|
||||
" fixed-point numbers with less than ",
|
||||
if_signed_unsigned!($Signedness, 4, 3),
|
||||
" integer bits.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::consts;
|
||||
use fixed::types::extra::U",
|
||||
if_signed_unsigned!($Signedness, $nm4, $nm3),
|
||||
";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U",
|
||||
if_signed_unsigned!($Signedness, $nm4, $nm3),
|
||||
">;
|
||||
assert_eq!(Fix::TAU, Fix::from_num(consts::TAU));
|
||||
assert!(4 <= Fix::TAU && Fix::TAU < 8);
|
||||
```
|
||||
|
||||
The following example fails to compile, since the maximum
|
||||
representable value with ",
|
||||
if_signed_unsigned!($Signedness, $nm3, $nm2),
|
||||
" fractional bits and ",
|
||||
if_signed_unsigned!($Signedness, 3, 2),
|
||||
" integer bits is < 4.
|
||||
|
||||
```rust,compile_fail
|
||||
use fixed::consts;
|
||||
use fixed::types::extra::U",
|
||||
if_signed_unsigned!($Signedness, $nm3, $nm2),
|
||||
";
|
||||
use fixed::", stringify!($Self), ";
|
||||
type Fix = ", stringify!($Self), "<U",
|
||||
if_signed_unsigned!($Signedness, $nm3, $nm2),
|
||||
">;
|
||||
let _ = Fix::TAU;
|
||||
```
|
||||
";
|
||||
impl<Frac: Unsigned> $Self<Frac>
|
||||
where
|
||||
Frac: IsLessOrEqual<$LeEqUC3, Output = True>,
|
||||
{
|
||||
/// A turn, τ = 6.28318…
|
||||
pub const TAU: $Self<Frac> = Self::from_const(consts::TAU);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Vendored
+3411
File diff suppressed because it is too large.
Load diff
Vendored
+2523
File diff suppressed because it is too large.
Load diff
Vendored
+5626
File diff suppressed because it is too large.
Load diff
Vendored
+1117
File diff suppressed because it is too large.
Load diff
Vendored
+503
@@ -0,0 +1,503 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or modify it under
|
||||
// the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT License
|
||||
// along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::float_helper;
|
||||
use crate::helpers::{FloatKind, FromFloatHelper, Private};
|
||||
use crate::int_helper::IntFixed;
|
||||
use crate::traits::{Fixed, FixedEquiv, FromFixed, ToFixed};
|
||||
use crate::types::extra::U0;
|
||||
use crate::{
|
||||
F128, F128Bits, FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32,
|
||||
FixedU64, FixedU128,
|
||||
};
|
||||
use bytemuck::TransparentWrapper;
|
||||
use half::{bf16 as half_bf16, f16 as half_f16};
|
||||
|
||||
impl ToFixed for bool {
|
||||
/// Converts a [`bool`] to a fixed-point number.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When debug assertions are enabled, panics if the value does
|
||||
/// not fit. When debug assertions are not enabled, the wrapped
|
||||
/// value can be returned, but it is not considered a breaking
|
||||
/// change if in the future it panics; if wrapping is required use
|
||||
/// [`wrapping_to_fixed`] instead.
|
||||
///
|
||||
/// [`wrapping_to_fixed`]: ToFixed::wrapping_to_fixed
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn to_fixed<F: Fixed>(self) -> F {
|
||||
ToFixed::to_fixed(u8::from(self))
|
||||
}
|
||||
|
||||
/// Converts a [`bool`] to a fixed-point number if it fits, otherwise returns [`None`].
|
||||
#[inline]
|
||||
fn checked_to_fixed<F: Fixed>(self) -> Option<F> {
|
||||
ToFixed::checked_to_fixed(u8::from(self))
|
||||
}
|
||||
|
||||
/// Convert a [`bool`] to a fixed-point number, saturating if it does not fit.
|
||||
#[inline]
|
||||
fn saturating_to_fixed<F: Fixed>(self) -> F {
|
||||
ToFixed::saturating_to_fixed(u8::from(self))
|
||||
}
|
||||
|
||||
/// Converts a [`bool`] to a fixed-point number, wrapping if it does not fit.
|
||||
#[inline]
|
||||
fn wrapping_to_fixed<F: Fixed>(self) -> F {
|
||||
ToFixed::wrapping_to_fixed(u8::from(self))
|
||||
}
|
||||
|
||||
/// Converts a [`bool`] to a fixed-point number.
|
||||
///
|
||||
/// Returns a [tuple] of the fixed-point number and a [`bool`]
|
||||
/// indicating whether an overflow has occurred. On overflow, the
|
||||
/// wrapped value is returned.
|
||||
#[inline]
|
||||
fn overflowing_to_fixed<F: Fixed>(self) -> (F, bool) {
|
||||
ToFixed::overflowing_to_fixed(u8::from(self))
|
||||
}
|
||||
|
||||
/// Converts a [`bool`] to a fixed-point number, panicking if it
|
||||
/// does not fit.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value does not fit, even when debug assertions
|
||||
/// are not enabled.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn unwrapped_to_fixed<F: Fixed>(self) -> F {
|
||||
ToFixed::unwrapped_to_fixed(u8::from(self))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_int {
|
||||
($Int:ident $(, $Equiv:ident)?) => {
|
||||
impl FromFixed for $Int {
|
||||
/// Converts a fixed-point number to an integer.
|
||||
///
|
||||
/// Any fractional bits are discarded, which rounds towards −∞.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When debug assertions are enabled, panics if the value
|
||||
/// does not fit. When debug assertions are not enabled,
|
||||
/// the wrapped value can be returned, but it is not
|
||||
/// considered a breaking change if in the future it
|
||||
/// panics; if wrapping is required use
|
||||
/// [`wrapping_from_fixed`] instead.
|
||||
///
|
||||
/// [`wrapping_from_fixed`]: FromFixed::wrapping_from_fixed
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn from_fixed<F: Fixed>(src: F) -> Self {
|
||||
IntFixed::<$Int>::int(FromFixed::from_fixed(src))
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to an integer if it fits, otherwise returns [`None`].
|
||||
///
|
||||
/// Any fractional bits are discarded, which rounds towards −∞.
|
||||
#[inline]
|
||||
fn checked_from_fixed<F: Fixed>(src: F) -> Option<Self> {
|
||||
FromFixed::checked_from_fixed(src).map(IntFixed::<$Int>::int)
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to an integer, saturating if it does not fit.
|
||||
///
|
||||
/// Any fractional bits are discarded, which rounds towards −∞.
|
||||
#[inline]
|
||||
fn saturating_from_fixed<F: Fixed>(src: F) -> Self {
|
||||
IntFixed::<$Int>::int(FromFixed::saturating_from_fixed(src))
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to an integer, wrapping if it does not fit.
|
||||
///
|
||||
/// Any fractional bits are discarded, which rounds towards −∞.
|
||||
#[inline]
|
||||
fn wrapping_from_fixed<F: Fixed>(src: F) -> Self {
|
||||
IntFixed::<$Int>::int(FromFixed::wrapping_from_fixed(src))
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to an integer.
|
||||
///
|
||||
/// Returns a [tuple] of the value and a [`bool`] indicating whether
|
||||
/// an overflow has occurred. On overflow, the wrapped value is
|
||||
/// returned.
|
||||
///
|
||||
/// Any fractional bits are discarded, which rounds towards −∞.
|
||||
#[inline]
|
||||
fn overflowing_from_fixed<F: Fixed>(src: F) -> (Self, bool) {
|
||||
let (fixed, overflow) = FromFixed::overflowing_from_fixed(src);
|
||||
(IntFixed::<$Int>::int(fixed), overflow)
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to an integer, panicking if it does not fit.
|
||||
///
|
||||
/// Any fractional bits are discarded, which rounds towards −∞.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value
|
||||
/// does not fit, even when debug assertions are not enabled.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn unwrapped_from_fixed<F: Fixed>(src: F) -> Self {
|
||||
IntFixed::<$Int>::int(FromFixed::unwrapped_from_fixed(src))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToFixed for $Int {
|
||||
/// Converts an integer to a fixed-point number.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When debug assertions are enabled, panics if the value
|
||||
/// does not fit. When debug assertions are not enabled,
|
||||
/// the wrapped value can be returned, but it is not
|
||||
/// considered a breaking change if in the future it
|
||||
/// panics; if wrapping is required use
|
||||
/// [`wrapping_to_fixed`] instead.
|
||||
///
|
||||
/// [`wrapping_to_fixed`]: ToFixed::wrapping_to_fixed
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn to_fixed<F: Fixed>(self) -> F {
|
||||
ToFixed::to_fixed(IntFixed(self).fixed())
|
||||
}
|
||||
|
||||
/// Converts an integer to a fixed-point number if it fits, otherwise returns [`None`].
|
||||
#[inline]
|
||||
fn checked_to_fixed<F: Fixed>(self) -> Option<F> {
|
||||
ToFixed::checked_to_fixed(IntFixed(self).fixed())
|
||||
}
|
||||
|
||||
/// Converts an integer to a fixed-point number, saturating if it does not fit.
|
||||
#[inline]
|
||||
fn saturating_to_fixed<F: Fixed>(self) -> F {
|
||||
ToFixed::saturating_to_fixed(IntFixed(self).fixed())
|
||||
}
|
||||
|
||||
/// Converts an integer to a fixed-point number, wrapping if it does not fit.
|
||||
#[inline]
|
||||
fn wrapping_to_fixed<F: Fixed>(self) -> F {
|
||||
ToFixed::wrapping_to_fixed(IntFixed(self).fixed())
|
||||
}
|
||||
|
||||
/// Converts an integer to a fixed-point number.
|
||||
///
|
||||
/// Returns a [tuple] of the fixed-point number and a [`bool`]
|
||||
/// indicating whether an overflow has occurred. On overflow, the
|
||||
/// wrapped value is returned.
|
||||
#[inline]
|
||||
fn overflowing_to_fixed<F: Fixed>(self) -> (F, bool) {
|
||||
ToFixed::overflowing_to_fixed(IntFixed(self).fixed())
|
||||
}
|
||||
|
||||
/// Converts an integer to a fixed-point number, panicking if it does not fit.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value does not fit, even when debug
|
||||
/// assertions are not enabled.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn unwrapped_to_fixed<F: Fixed>(self) -> F {
|
||||
ToFixed::unwrapped_to_fixed(IntFixed(self).fixed())
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl FixedEquiv for $Int {
|
||||
type Equiv = $Equiv<U0>;
|
||||
|
||||
#[inline]
|
||||
fn to_fixed_equiv(self) -> $Equiv<U0> {
|
||||
$Equiv::from_bits(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_fixed_equiv(&self) -> &$Equiv<U0> {
|
||||
$Equiv::wrap_ref(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_fixed_equiv_mut(&mut self) -> &mut $Equiv<U0> {
|
||||
$Equiv::wrap_mut(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_fixed_equiv(f: $Equiv<U0>) -> $Int {
|
||||
f.to_bits()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ref_from_fixed_equiv(f: &$Equiv<U0>) -> &$Int {
|
||||
&f.bits
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mut_from_fixed_equiv(f: &mut $Equiv<U0>) -> &mut $Int {
|
||||
&mut f.bits
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_int! { i8, FixedI8 }
|
||||
impl_int! { i16, FixedI16 }
|
||||
impl_int! { i32, FixedI32 }
|
||||
impl_int! { i64, FixedI64 }
|
||||
impl_int! { i128, FixedI128 }
|
||||
impl_int! { isize }
|
||||
impl_int! { u8, FixedU8 }
|
||||
impl_int! { u16, FixedU16 }
|
||||
impl_int! { u32, FixedU32 }
|
||||
impl_int! { u64, FixedU64 }
|
||||
impl_int! { u128, FixedU128 }
|
||||
impl_int! { usize }
|
||||
|
||||
macro_rules! impl_float {
|
||||
($Float:ident, $link:expr, $overflows_fmt:expr, $overflows_filt:expr) => {
|
||||
impl FromFixed for $Float {
|
||||
/// Converts a fixed-point number to a floating-point number.
|
||||
///
|
||||
/// Rounding is to the nearest, with ties rounded to even.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When debug assertions are enabled, panics if the value
|
||||
/// does not fit. When debug assertions are not enabled,
|
||||
/// the wrapped value can be returned, but it is not
|
||||
/// considered a breaking change if in the future it
|
||||
/// panics; if wrapping is required use
|
||||
/// [`wrapping_from_fixed`] instead.
|
||||
///
|
||||
/// [`wrapping_from_fixed`]: FromFixed::wrapping_from_fixed
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn from_fixed<F: Fixed>(src: F) -> Self {
|
||||
let helper = src.to_float_helper(Private);
|
||||
float_helper::$Float::from_to_float_helper(helper, F::FRAC_NBITS, F::INT_NBITS)
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to a floating-point
|
||||
/// number if it fits, otherwise returns [`None`].
|
||||
///
|
||||
/// Rounding is to the nearest, with ties rounded to even.
|
||||
#[inline]
|
||||
fn checked_from_fixed<F: Fixed>(src: F) -> Option<Self> {
|
||||
Some(FromFixed::from_fixed(src))
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to a floating-point
|
||||
/// number, saturating if it does not fit.
|
||||
///
|
||||
/// Rounding is to the nearest, with ties rounded to even.
|
||||
#[inline]
|
||||
fn saturating_from_fixed<F: Fixed>(src: F) -> Self {
|
||||
FromFixed::from_fixed(src)
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to a floating-point
|
||||
/// number, wrapping if it does not fit.
|
||||
///
|
||||
/// Rounding is to the nearest, with ties rounded to even.
|
||||
#[inline]
|
||||
fn wrapping_from_fixed<F: Fixed>(src: F) -> Self {
|
||||
FromFixed::from_fixed(src)
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to a floating-point number.
|
||||
///
|
||||
/// Returns a [tuple] of the value and a [`bool`]
|
||||
/// indicating whether an overflow has occurred. On
|
||||
/// overflow, the wrapped value is returned.
|
||||
///
|
||||
/// Rounding is to the nearest, with ties rounded to even.
|
||||
#[inline]
|
||||
fn overflowing_from_fixed<F: Fixed>(src: F) -> (Self, bool) {
|
||||
(FromFixed::from_fixed(src), false)
|
||||
}
|
||||
|
||||
/// Converts a fixed-point number to a floating-point
|
||||
/// number, panicking if it does not fit.
|
||||
///
|
||||
/// Rounding is to the nearest, with ties rounded to even.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value does not fit, even when debug
|
||||
/// assertions are not enabled.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn unwrapped_from_fixed<F: Fixed>(src: F) -> Self {
|
||||
FromFixed::from_fixed(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToFixed for $Float {
|
||||
comment! {
|
||||
"Converts a floating-point number to a fixed-point number.
|
||||
|
||||
Rounding is to the nearest, with ties rounded to even.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if `self` is not [finite].
|
||||
|
||||
When debug assertions are enabled, also panics if the value does not
|
||||
fit. When debug assertions are not enabled, the wrapped value can be
|
||||
returned, but it is not considered a breaking change if in the future
|
||||
it panics; if wrapping is required use [`wrapping_to_fixed`] instead.
|
||||
|
||||
[`wrapping_to_fixed`]: ToFixed::wrapping_to_fixed
|
||||
[finite]: ", $link, "::is_finite
|
||||
";
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn to_fixed<F: Fixed>(self) -> F {
|
||||
let (wrapped, overflow) = ToFixed::overflowing_to_fixed(self);
|
||||
debug_assert!(!overflow, $overflows_fmt, $overflows_filt(self));
|
||||
wrapped
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a floating-point number to a fixed-point
|
||||
/// number if it fits, otherwise returns [`None`].
|
||||
///
|
||||
/// Rounding is to the nearest, with ties rounded to even.
|
||||
#[inline]
|
||||
fn checked_to_fixed<F: Fixed>(self) -> Option<F> {
|
||||
let kind = float_helper::$Float::to_float_kind(self, F::FRAC_NBITS, F::INT_NBITS);
|
||||
match kind {
|
||||
FloatKind::Finite { .. } => {
|
||||
let helper = FromFloatHelper { kind };
|
||||
match F::overflowing_from_float_helper(Private, helper) {
|
||||
(_, true) => None,
|
||||
(wrapped, false) => Some(wrapped),
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Converts a floating-point number to a fixed-point
|
||||
number, saturating if it does not fit.
|
||||
|
||||
Rounding is to the nearest, with ties rounded to even.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if `self` is [NaN].
|
||||
|
||||
[NaN]: ", $link, "::is_nan
|
||||
";
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn saturating_to_fixed<F: Fixed>(self) -> F {
|
||||
let kind =
|
||||
float_helper::$Float::to_float_kind(self, F::FRAC_NBITS, F::INT_NBITS);
|
||||
let helper = FromFloatHelper { kind };
|
||||
F::saturating_from_float_helper(Private, helper)
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Converts a floating-point number to a fixed-point
|
||||
number, wrapping if it does not fit.
|
||||
|
||||
Rounding is to the nearest, with ties rounded to even.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if `self` is not [finite].
|
||||
|
||||
[finite]: ", $link, "::is_finite
|
||||
";
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn wrapping_to_fixed<F: Fixed>(self) -> F {
|
||||
let (wrapped, _) = ToFixed::overflowing_to_fixed(self);
|
||||
wrapped
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Converts a floating-point number to a fixed-point number.
|
||||
|
||||
Returns a [tuple] of the fixed-point number and a [`bool`] indicating
|
||||
whether an overflow has occurred. On overflow, the wrapped value is
|
||||
returned.
|
||||
|
||||
Rounding is to the nearest, with ties rounded to even.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if `self` is not [finite].
|
||||
|
||||
[finite]: ", $link, "::is_finite
|
||||
";
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn overflowing_to_fixed<F: Fixed>(self) -> (F, bool) {
|
||||
let kind =
|
||||
float_helper::$Float::to_float_kind(self, F::FRAC_NBITS, F::INT_NBITS);
|
||||
let helper = FromFloatHelper { kind };
|
||||
F::overflowing_from_float_helper(Private, helper)
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Converts a floating-point number to a fixed-point
|
||||
number, panicking if it does not fit.
|
||||
|
||||
Rounding is to the nearest, with ties rounded to even.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if `self` is not [finite] or if the value does not fit, even
|
||||
when debug assertions are not enabled.
|
||||
|
||||
[finite]: ", $link, "::is_finite
|
||||
";
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
fn unwrapped_to_fixed<F: Fixed>(self) -> F {
|
||||
match ToFixed::overflowing_to_fixed(self) {
|
||||
(val, false) => val,
|
||||
(_, true) => panic!("overflow"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly-float")]
|
||||
impl_float! { f16, "f16", "f16::from_bits(0x{:04X}) overflows", |x: f16| x.to_bits() }
|
||||
impl_float! { half_f16, "half::f16", "{} overflows", |x| x }
|
||||
impl_float! { half_bf16, "half::bf16", "{} overflows", |x| x }
|
||||
impl_float! { f32, "f32", "{} overflows", |x| x }
|
||||
impl_float! { f64, "f64", "{} overflows", |x| x }
|
||||
#[cfg(feature = "nightly-float")]
|
||||
impl_float! { f128, "f128", "f128::from_bits(0x{:032X}) overflows", |x: f128| x.to_bits() }
|
||||
impl_float! { F128, "F128", "F128::from_bits(0x{:032X}) overflows", |x: F128| x.to_bits() }
|
||||
impl_float! { F128Bits, "f64", "F128Bits({}) overflows", |x: F128Bits| x.0 }
|
||||
Vendored
+2257
File diff suppressed because it is too large.
Load diff
Vendored
+171
@@ -0,0 +1,171 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::types::extra::{LeEqU8, LeEqU16, LeEqU32, LeEqU64, LeEqU128};
|
||||
use crate::{
|
||||
FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
|
||||
FixedU128, Unwrapped, Wrapping,
|
||||
};
|
||||
use serde::de::{Deserialize, Deserializer, Error as DeError};
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
#[cfg(not(feature = "serde-str"))]
|
||||
use {
|
||||
core::fmt::{Formatter, Result as FmtResult},
|
||||
serde::de::{MapAccess, SeqAccess, Visitor},
|
||||
serde::ser::SerializeStruct,
|
||||
};
|
||||
|
||||
macro_rules! serde_fixed {
|
||||
($Fixed:ident($LeEqU:ident) is $TBits:ident name $Name:expr) => {
|
||||
impl<Frac: $LeEqU> Serialize for $Fixed<Frac> {
|
||||
#[cfg(not(feature = "serde-str"))]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let bits = self.to_bits();
|
||||
let mut state = serializer.serialize_struct($Name, 1)?;
|
||||
state.serialize_field("bits", &bits)?;
|
||||
state.end()
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-str")]
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
if serializer.is_human_readable() {
|
||||
self.to_string().serialize(serializer)
|
||||
} else {
|
||||
self.to_bits().serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> Serialize for Wrapping<$Fixed<Frac>> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Frac: $LeEqU> Serialize for Unwrapped<$Fixed<Frac>> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, Frac: $LeEqU> Deserialize<'de> for $Fixed<Frac> {
|
||||
#[cfg(not(feature = "serde-str"))]
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
struct FixedVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for FixedVisitor {
|
||||
type Value = $TBits;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
|
||||
formatter.write_str("struct ")?;
|
||||
formatter.write_str($Name)
|
||||
}
|
||||
|
||||
fn visit_seq<V: SeqAccess<'de>>(self, mut seq: V) -> Result<$TBits, V::Error> {
|
||||
let bits = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| DeError::invalid_length(0, &self))?;
|
||||
Ok(bits)
|
||||
}
|
||||
|
||||
fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<$TBits, V::Error> {
|
||||
let mut bits = None;
|
||||
while let Some(key) = map.next_key()? {
|
||||
match key {
|
||||
Field::Bits => {
|
||||
if bits.is_some() {
|
||||
return Err(DeError::duplicate_field("bits"));
|
||||
}
|
||||
bits = Some(map.next_value()?);
|
||||
}
|
||||
}
|
||||
}
|
||||
let bits = bits.ok_or_else(|| DeError::missing_field("bits"))?;
|
||||
Ok(bits)
|
||||
}
|
||||
}
|
||||
|
||||
let bits = deserializer.deserialize_struct($Name, FIELDS, FixedVisitor)?;
|
||||
Ok($Fixed::from_bits(bits))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-str")]
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
if deserializer.is_human_readable() {
|
||||
String::deserialize(deserializer)?
|
||||
.parse()
|
||||
.map_err(|e| DeError::custom(format_args!("parse error: {e}")))
|
||||
} else {
|
||||
$TBits::deserialize(deserializer).map($Fixed::from_bits)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, Frac: $LeEqU> Deserialize<'de> for Wrapping<$Fixed<Frac>> {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
$Fixed::deserialize(deserializer).map(Wrapping)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, Frac: $LeEqU> Deserialize<'de> for Unwrapped<$Fixed<Frac>> {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
$Fixed::deserialize(deserializer).map(Unwrapped)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
serde_fixed! { FixedI8(LeEqU8) is i8 name "FixedI8" }
|
||||
serde_fixed! { FixedI16(LeEqU16) is i16 name "FixedI16" }
|
||||
serde_fixed! { FixedI32(LeEqU32) is i32 name "FixedI32" }
|
||||
serde_fixed! { FixedI64(LeEqU64) is i64 name "FixedI64" }
|
||||
serde_fixed! { FixedI128(LeEqU128) is i128 name "FixedI128" }
|
||||
serde_fixed! { FixedU8(LeEqU8) is u8 name "FixedU8" }
|
||||
serde_fixed! { FixedU16(LeEqU16) is u16 name "FixedU16" }
|
||||
serde_fixed! { FixedU32(LeEqU32) is u32 name "FixedU32" }
|
||||
serde_fixed! { FixedU64(LeEqU64) is u64 name "FixedU64" }
|
||||
serde_fixed! { FixedU128(LeEqU128) is u128 name "FixedU128" }
|
||||
|
||||
#[cfg(not(feature = "serde-str"))]
|
||||
const FIELDS: &[&str] = &["bits"];
|
||||
|
||||
#[cfg(not(feature = "serde-str"))]
|
||||
enum Field {
|
||||
Bits,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "serde-str"))]
|
||||
impl<'de> Deserialize<'de> for Field {
|
||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Field, D::Error> {
|
||||
struct FieldVisitor;
|
||||
|
||||
impl Visitor<'_> for FieldVisitor {
|
||||
type Value = Field;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
|
||||
formatter.write_str("`bits`")
|
||||
}
|
||||
|
||||
fn visit_str<E: DeError>(self, value: &str) -> Result<Field, E> {
|
||||
match value {
|
||||
"bits" => Ok(Field::Bits),
|
||||
_ => Err(DeError::unknown_field(value, FIELDS)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_identifier(FieldVisitor)
|
||||
}
|
||||
}
|
||||
Vendored
+521
@@ -0,0 +1,521 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use core::num::NonZero;
|
||||
|
||||
// The mathematics below is based on the comments from FreeBSD's
|
||||
// /usr/src/lib/msun/src/e_sqrt.c.
|
||||
|
||||
// q_i = sqrt(y) truncated to i bits after point.
|
||||
// q_0 = 1
|
||||
// y_i = 2^i (y - q_i^2)
|
||||
// y_0 = y - 1
|
||||
//
|
||||
// If (q_i + 1>>(i+1))^2 <= y:
|
||||
// q_(i+1) = q_i + 1>>(i+1)
|
||||
// Else:
|
||||
// q_(i+1) = q_i
|
||||
//
|
||||
// Equivalently:
|
||||
//
|
||||
// If q_i + 1>>(i+2) <= y_i:
|
||||
// q_(i+1) = q_i + 1>>(i+1)
|
||||
// y_(i+1) = 2 (y_i - q_i - 1>>(i+2))
|
||||
// Else:
|
||||
// q_i+1 = q_i
|
||||
// y_i+1 = 2 y_i
|
||||
//
|
||||
// * Iterations do not include q_0, y_0 as they are initialization.
|
||||
// * i goes from 1 to iter.
|
||||
// * Both q and y are stored with 2 integer bits. q is in range [1, 2); y is
|
||||
// in range [1, 4).
|
||||
// * 1>>(i+2) needs special code when i + 2 > nbits - 2. Since maximum iter is
|
||||
// nbits - 1, i + 2 can be nbits + 1 which is > nbits - 2 by 2.
|
||||
//
|
||||
// Some examples for u8.
|
||||
//
|
||||
// frac_nbits == 0:
|
||||
// sip = 4 - leading / 2
|
||||
// 4 significant int pairs: 0100 0000. -> 0000 1000. (y << 0, 3 iter, q >> 3)
|
||||
// 3 significant int pairs: 0001 0000. -> 0000 0100. (y << 2, 2 iter, q >> 4)
|
||||
// 2 significant int pairs: 0000 0100. -> 0000 0010. (y << 4, 1 iter, q >> 5)
|
||||
// 1 significant int pairs: 0000 0001. -> 0000 0001. (y << 6, 0 iter, q >> 6)
|
||||
// General: y << 8 - 2sip, -1 + sip iter, q >> 7 - sip
|
||||
//
|
||||
// frac_nbits == 1:
|
||||
// sip = 4 - (leading + 1) / 2
|
||||
// 4 significant int pairs: 100 0000.0 -> 000 1000.0 (y >> 1, 4 iter, q >> 2)
|
||||
// 3 significant int pairs: 001 0000.0 -> 000 0100.0 (y << 1, 3 iter, q >> 3)
|
||||
// 2 significant int pairs: 000 0100.0 -> 000 0010.0 (y << 3, 2 iter, q >> 4)
|
||||
// 1 significant int pairs: 000 0001.0 -> 000 0001.0 (y << 5, 1 iter, q >> 5)
|
||||
// 0 significant int pairs: 000 0000.1 -> 000 0000.1 (y << 7, 0 iter, q >> 6)
|
||||
// General: y << 7 - 2sip, sip iter, q >> 6 - sip
|
||||
//
|
||||
// frac_nbits == 2:
|
||||
// sip = 3 - leading / 2
|
||||
// 3 significant int pairs: 01 0000.00 -> 00 0100.00 (y << 0, 4 iter, q >> 2)
|
||||
// 2 significant int pairs: 00 0100.00 -> 00 0010.00 (y << 2, 3 iter, q >> 3)
|
||||
// 1 significant int pairs: 00 0001.00 -> 00 0001.00 (y << 4, 2 iter, q >> 4)
|
||||
// 0 significant int pairs: 00 0000.01 -> 00 0000.10 (y << 6, 1 iter, q >> 5)
|
||||
// General: y << 6 - 2sip, 1 + sip iter, q >> 5 - sip
|
||||
//
|
||||
// frac_nbits = 3:
|
||||
// sip = 3 - (leading + 1) / 2
|
||||
// 3 significant int pairs: 1 0000.000 -> 0 0100.000 (y >> 1, 5 iter, q >> 1)
|
||||
// 2 significant int pairs: 0 0100.000 -> 0 0010.000 (y << 1, 4 iter, q >> 2)
|
||||
// 1 significant int pairs: 0 0001.000 -> 0 0001.000 (y << 3, 3 iter, q >> 3)
|
||||
// 0 significant int pairs: 0 0000.010 -> 0 0000.100 (y << 5, 2 iter, q >> 4)
|
||||
// -1 significant int pairs: 0 0000.001 -> 0 0000.010 (y << 7, 1 iter, q >> 5)
|
||||
// General: y << 5 - 2sip, 2 + sip iter, q >> 4 - sip
|
||||
//
|
||||
// frac_nbits == 4:
|
||||
// sip = 2 - leading / 2
|
||||
// 2 significant int pairs: 0100.0000 -> 0010.0000 (y << 0, 5 iter, q >> 1)
|
||||
// 1 significant int pairs: 0001.0000 -> 0001.0000 (y << 2, 4 iter, q >> 2)
|
||||
// 0 significant int pairs: 0000.0100 -> 0000.1000 (y << 4, 3 iter, q >> 3)
|
||||
// -1 significant int pairs: 0000.0001 -> 0000.0100 (y << 6, 2 iter, q >> 4)
|
||||
// General: y << 4 - 2sip, 3 + sip iter, q >> 3 - sip
|
||||
//
|
||||
// frac_nbits = 5:
|
||||
// sip = 2 - (leading + 1) / 2
|
||||
// 2 significant int pairs: 100.0000 0 -> 010.0000 0 (y >> 1, 6 iter, q >> 0)
|
||||
// 1 significant int pairs: 001.0000 0 -> 001.0000 0 (y << 1, 5 iter, q >> 1)
|
||||
// 0 significant int pairs: 000.0100 0 -> 000.1000 0 (y << 3, 4 iter, q >> 2)
|
||||
// -1 significant int pairs: 000.0001 0 -> 000.0100 0 (y << 5, 3 iter, q >> 3)
|
||||
// -2 significant int pairs: 000.0000 1 -> 000.0010 1 (y << 7, 2 iter, q >> 4)
|
||||
// General: y << 3 - 2sip, 4 + sip iter, q >> 2 - sip
|
||||
//
|
||||
// frac_nbits == 6:
|
||||
// sip = 1 - leading / 2
|
||||
// 1 significant int pairs: 01.0000 00 -> 01.0000 00 (y << 0, 6 iter, q >> 0)
|
||||
// 0 significant int pairs: 00.0100 00 -> 00.1000 00 (y << 2, 5 iter, q >> 1)
|
||||
// -1 significant int pairs: 00.0001 00 -> 00.0100 00 (y << 4, 4 iter, q >> 2)
|
||||
// -2 significant int pairs: 00.0000 01 -> 00.0010 00 (y << 6, 3 iter, q >> 3)
|
||||
// General: y << 2 - 2sip, 5 + sip iter, q >> 1 - sip
|
||||
//
|
||||
// frac_nbits == 7:
|
||||
// sip = 1 - (leading + 1) / 2
|
||||
// 1 significant int pairs: 1.0000 000 -> 1.0000 000 (y >> 1, 7 iter, q << 1)
|
||||
// 0 significant int pairs: 0.0100 000 -> 0.1000 000 (y << 1, 6 iter, q >> 0)
|
||||
// -1 significant int pairs: 0.0001 000 -> 0.0100 000 (y << 3, 5 iter, q >> 1)
|
||||
// -2 significant int pairs: 0.0000 010 -> 0.0010 000 (y << 5, 4 iter, q >> 2)
|
||||
// -3 significant int pairs: 0.0000 001 -> 0.0001 011 (y << 7, 3 iter, q >> 3)
|
||||
// General: y << 1 - 2sip, 6 + sip iter, q >> -sip
|
||||
//
|
||||
// frac_nbits == 8:
|
||||
// sip = 0 - leading / 2
|
||||
// 0 significant int pairs: .0100 0000 -> .1000 0000 (y << 0, 7 iter, q << 1)
|
||||
// -1 significant int pairs: .0001 0000 -> .0100 0000 (y << 2, 6 iter, q >> 0)
|
||||
// -2 significant int pairs: .0000 0100 -> .0010 0000 (y << 4, 5 iter, q >> 1)
|
||||
// -3 significant int pairs: .0000 0001 -> .0001 0000 (y << 6, 4 iter, q >> 2)
|
||||
// General: y << -2sip, 7 + sip iter, q >> -1 - sip
|
||||
//
|
||||
// General:
|
||||
// Even frac_nbits:
|
||||
// sip = int_nbits / 2 - leading / 2
|
||||
// Odd frac_nbits:
|
||||
// sip = (int_nbits + 1) / 2 - (leading + 1) / 2
|
||||
// Then:
|
||||
// y << int_nbits - 2sip, frac_nbits - 1 + sip iter, q >> int_nbits - 1 - sip
|
||||
|
||||
macro_rules! impl_sqrt {
|
||||
($u:ident) => {
|
||||
pub const fn $u(val: NonZero<$u>, frac_nbits: u32) -> $u {
|
||||
let int_nbits = $u::BITS - frac_nbits;
|
||||
let odd_frac_nbits = frac_nbits % 2 != 0;
|
||||
let leading = val.leading_zeros();
|
||||
let sig_int_pairs = if odd_frac_nbits {
|
||||
int_nbits.div_ceil(2) as i32 - leading.div_ceil(2) as i32
|
||||
} else {
|
||||
(int_nbits / 2) as i32 - (leading / 2) as i32
|
||||
};
|
||||
|
||||
let mut i = 1;
|
||||
let mut q_i = 1 << ($u::BITS - 2);
|
||||
let mut next_bit = q_i >> 1;
|
||||
let mut y_i = val.get();
|
||||
let input_shl = int_nbits as i32 - sig_int_pairs * 2;
|
||||
if input_shl < 0 {
|
||||
// This can only happen when we have odd frac_nbits and the most
|
||||
// significant bit is set. We would need to shift right by 1.
|
||||
debug_assert!(input_shl == -1);
|
||||
|
||||
// Do one iteration here as this is a special case.
|
||||
|
||||
// In this special case, y is in the range [1, 2) instead of [1, 4),
|
||||
// and q is in the range [1, √2) instead of [1, 2).
|
||||
// Therefore, q_1 is always 0b1.0, and never 0b1.1.
|
||||
// Since q_0 = q_1 = 1, y_1 = 2 × (y - q_1^2) = 2 × y - 2 × q_i.
|
||||
// Since input_shl is -1, its effect is cancelled out by 2 × y,
|
||||
// and we only need to subtract 2 × q_i from y_i.
|
||||
y_i -= 2 * q_i;
|
||||
next_bit >>= 1;
|
||||
i += 1;
|
||||
} else {
|
||||
y_i <<= input_shl;
|
||||
y_i -= q_i;
|
||||
};
|
||||
|
||||
let iters = (frac_nbits as i32 - 1 + sig_int_pairs) as u32;
|
||||
while i <= iters {
|
||||
let d = next_bit >> 1;
|
||||
if d == 0 {
|
||||
if i == iters {
|
||||
// Here result_shr must be 0, otherwise we wouldn't have
|
||||
// room to potentially insert one extra bit.
|
||||
debug_assert!(int_nbits as i32 - 1 - sig_int_pairs == 0);
|
||||
|
||||
// d == 0.5, so we really need q_i + 0.5 <= y_i,
|
||||
// which can be obtained with q_i < y_i
|
||||
if q_i < y_i {
|
||||
q_i += 1;
|
||||
}
|
||||
|
||||
return q_i;
|
||||
}
|
||||
|
||||
debug_assert!(i == iters - 1);
|
||||
// Here result_shr must be -1, otherwise we wouldn't have
|
||||
// room to potentially insert two extra bits.
|
||||
debug_assert!(int_nbits as i32 - 1 - sig_int_pairs == -1);
|
||||
|
||||
// d == 0.5, so we really need q_i + 0.5 <= y_i,
|
||||
// which can be obtained with q_i < y_i
|
||||
if q_i < y_i {
|
||||
// We cannot subtract d == 0.5 from y_i immediately, so
|
||||
// we subtract 1 from y_i before the multiplication by 2
|
||||
// and then add 1 back. (There may be a potential overflow
|
||||
// if we multiply y_i by 2 and then subtract 1.)
|
||||
y_i -= q_i + 1;
|
||||
y_i *= 2;
|
||||
y_i += 1;
|
||||
q_i += 1;
|
||||
} else {
|
||||
y_i *= 2;
|
||||
}
|
||||
|
||||
// d == 0.25, so we really need q_i + 0.25 <= y_i,
|
||||
// which can be obtained with q_i < y_i
|
||||
if q_i < y_i {
|
||||
// We cannot add next_bit == 0.5 to q_i immediately, so
|
||||
// we add 1 to q_i after the left shift.
|
||||
q_i = (q_i << 1) + 1;
|
||||
} else {
|
||||
q_i <<= 1;
|
||||
}
|
||||
|
||||
return q_i;
|
||||
}
|
||||
|
||||
if q_i + d <= y_i {
|
||||
y_i -= q_i + d;
|
||||
q_i += next_bit;
|
||||
}
|
||||
y_i *= 2;
|
||||
|
||||
next_bit = d;
|
||||
i += 1;
|
||||
}
|
||||
let result_shr = int_nbits as i32 - 1 - sig_int_pairs;
|
||||
q_i >> result_shr
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_sqrt! { u8 }
|
||||
impl_sqrt! { u16 }
|
||||
impl_sqrt! { u32 }
|
||||
impl_sqrt! { u64 }
|
||||
impl_sqrt! { u128 }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::extra::{
|
||||
U0, U1, U3, U4, U5, U6, U7, U8, U9, U13, U14, U15, U16, U17, U29, U30, U31, U32, U33, U61,
|
||||
U62, U63, U64, U65, U125, U126, U127, U128,
|
||||
};
|
||||
use crate::{
|
||||
FixedI8, FixedI16, FixedI32, FixedI64, FixedI128, FixedU8, FixedU16, FixedU32, FixedU64,
|
||||
FixedU128,
|
||||
};
|
||||
|
||||
macro_rules! check_sqrt {
|
||||
($val:expr) => {{
|
||||
let sqrt = $val.sqrt();
|
||||
assert!(sqrt * sqrt <= $val);
|
||||
let delta = $val.wrapping_neg().wrapping_sub(!$val);
|
||||
if let Some(sqrt_delta) = sqrt.checked_add(delta) {
|
||||
if let Some(prod) = sqrt_delta.checked_mul(sqrt_delta) {
|
||||
assert!(prod >= $val);
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_max_8() {
|
||||
check_sqrt!(FixedU8::<U0>::MAX);
|
||||
check_sqrt!(FixedU8::<U1>::MAX);
|
||||
check_sqrt!(FixedU8::<U3>::MAX);
|
||||
check_sqrt!(FixedU8::<U4>::MAX);
|
||||
check_sqrt!(FixedU8::<U5>::MAX);
|
||||
check_sqrt!(FixedU8::<U7>::MAX);
|
||||
check_sqrt!(FixedU8::<U8>::MAX);
|
||||
assert_eq!(FixedU8::<U8>::MAX.sqrt(), FixedU8::<U8>::MAX);
|
||||
|
||||
check_sqrt!(FixedI8::<U0>::MAX);
|
||||
check_sqrt!(FixedI8::<U1>::MAX);
|
||||
check_sqrt!(FixedI8::<U3>::MAX);
|
||||
check_sqrt!(FixedI8::<U4>::MAX);
|
||||
check_sqrt!(FixedI8::<U5>::MAX);
|
||||
check_sqrt!(FixedI8::<U7>::MAX);
|
||||
assert!(FixedI8::<U8>::MAX.checked_sqrt().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_max_16() {
|
||||
check_sqrt!(FixedU16::<U0>::MAX);
|
||||
check_sqrt!(FixedU16::<U1>::MAX);
|
||||
check_sqrt!(FixedU16::<U7>::MAX);
|
||||
check_sqrt!(FixedU16::<U8>::MAX);
|
||||
check_sqrt!(FixedU16::<U9>::MAX);
|
||||
check_sqrt!(FixedU16::<U15>::MAX);
|
||||
check_sqrt!(FixedU16::<U16>::MAX);
|
||||
assert_eq!(FixedU16::<U16>::MAX.sqrt(), FixedU16::<U16>::MAX);
|
||||
|
||||
check_sqrt!(FixedI16::<U0>::MAX);
|
||||
check_sqrt!(FixedI16::<U1>::MAX);
|
||||
check_sqrt!(FixedI16::<U7>::MAX);
|
||||
check_sqrt!(FixedI16::<U8>::MAX);
|
||||
check_sqrt!(FixedI16::<U9>::MAX);
|
||||
check_sqrt!(FixedI16::<U15>::MAX);
|
||||
assert!(FixedI16::<U16>::MAX.checked_sqrt().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_max_32() {
|
||||
check_sqrt!(FixedU32::<U0>::MAX);
|
||||
check_sqrt!(FixedU32::<U1>::MAX);
|
||||
check_sqrt!(FixedU32::<U15>::MAX);
|
||||
check_sqrt!(FixedU32::<U16>::MAX);
|
||||
check_sqrt!(FixedU32::<U17>::MAX);
|
||||
check_sqrt!(FixedU32::<U31>::MAX);
|
||||
check_sqrt!(FixedU32::<U32>::MAX);
|
||||
assert_eq!(FixedU32::<U32>::MAX.sqrt(), FixedU32::<U32>::MAX);
|
||||
|
||||
check_sqrt!(FixedI32::<U0>::MAX);
|
||||
check_sqrt!(FixedI32::<U1>::MAX);
|
||||
check_sqrt!(FixedI32::<U15>::MAX);
|
||||
check_sqrt!(FixedI32::<U16>::MAX);
|
||||
check_sqrt!(FixedI32::<U17>::MAX);
|
||||
check_sqrt!(FixedI32::<U31>::MAX);
|
||||
assert!(FixedI32::<U32>::MAX.checked_sqrt().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_max_64() {
|
||||
check_sqrt!(FixedU64::<U0>::MAX);
|
||||
check_sqrt!(FixedU64::<U1>::MAX);
|
||||
check_sqrt!(FixedU64::<U31>::MAX);
|
||||
check_sqrt!(FixedU64::<U32>::MAX);
|
||||
check_sqrt!(FixedU64::<U33>::MAX);
|
||||
check_sqrt!(FixedU64::<U63>::MAX);
|
||||
check_sqrt!(FixedU64::<U64>::MAX);
|
||||
assert_eq!(FixedU64::<U64>::MAX.sqrt(), FixedU64::<U64>::MAX);
|
||||
|
||||
check_sqrt!(FixedI64::<U0>::MAX);
|
||||
check_sqrt!(FixedI64::<U1>::MAX);
|
||||
check_sqrt!(FixedI64::<U31>::MAX);
|
||||
check_sqrt!(FixedI64::<U32>::MAX);
|
||||
check_sqrt!(FixedI64::<U33>::MAX);
|
||||
check_sqrt!(FixedI64::<U63>::MAX);
|
||||
assert!(FixedI64::<U64>::MAX.checked_sqrt().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_max_128() {
|
||||
check_sqrt!(FixedU128::<U0>::MAX);
|
||||
check_sqrt!(FixedU128::<U1>::MAX);
|
||||
check_sqrt!(FixedU128::<U63>::MAX);
|
||||
check_sqrt!(FixedU128::<U64>::MAX);
|
||||
check_sqrt!(FixedU128::<U65>::MAX);
|
||||
check_sqrt!(FixedU128::<U127>::MAX);
|
||||
check_sqrt!(FixedU128::<U128>::MAX);
|
||||
assert_eq!(FixedU128::<U128>::MAX.sqrt(), FixedU128::<U128>::MAX);
|
||||
|
||||
check_sqrt!(FixedI128::<U0>::MAX);
|
||||
check_sqrt!(FixedI128::<U1>::MAX);
|
||||
check_sqrt!(FixedI128::<U63>::MAX);
|
||||
check_sqrt!(FixedI128::<U64>::MAX);
|
||||
check_sqrt!(FixedI128::<U65>::MAX);
|
||||
check_sqrt!(FixedI128::<U127>::MAX);
|
||||
assert!(FixedI128::<U128>::MAX.checked_sqrt().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_two_8() {
|
||||
assert_eq!(FixedU8::<U0>::from_num(2).sqrt(), FixedU8::<U0>::SQRT_2);
|
||||
assert_eq!(FixedU8::<U1>::from_num(2).sqrt(), FixedU8::<U1>::SQRT_2);
|
||||
assert_eq!(FixedU8::<U3>::from_num(2).sqrt(), FixedU8::<U3>::SQRT_2);
|
||||
assert_eq!(FixedU8::<U4>::from_num(2).sqrt(), FixedU8::<U4>::SQRT_2);
|
||||
assert_eq!(FixedU8::<U5>::from_num(2).sqrt(), FixedU8::<U5>::SQRT_2);
|
||||
assert_eq!(FixedU8::<U6>::from_num(2).sqrt(), FixedU8::<U6>::SQRT_2);
|
||||
assert!(
|
||||
FixedU8::<U7>::MAX.sqrt() == FixedU8::<U7>::SQRT_2 - FixedU8::<U7>::DELTA
|
||||
|| FixedU8::<U7>::MAX.sqrt() == FixedU8::<U7>::SQRT_2
|
||||
);
|
||||
|
||||
assert_eq!(FixedI8::<U0>::from_num(2).sqrt(), FixedI8::<U0>::SQRT_2);
|
||||
assert_eq!(FixedI8::<U1>::from_num(2).sqrt(), FixedI8::<U1>::SQRT_2);
|
||||
assert_eq!(FixedI8::<U3>::from_num(2).sqrt(), FixedI8::<U3>::SQRT_2);
|
||||
assert_eq!(FixedI8::<U4>::from_num(2).sqrt(), FixedI8::<U4>::SQRT_2);
|
||||
assert_eq!(FixedI8::<U5>::from_num(2).sqrt(), FixedI8::<U5>::SQRT_2);
|
||||
assert!(
|
||||
FixedI8::<U6>::MAX.sqrt() == FixedI8::<U6>::SQRT_2 - FixedI8::<U6>::DELTA
|
||||
|| FixedI8::<U6>::MAX.sqrt() == FixedI8::<U6>::SQRT_2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_two_16() {
|
||||
assert_eq!(FixedU16::<U0>::from_num(2).sqrt(), FixedU16::<U0>::SQRT_2);
|
||||
assert_eq!(FixedU16::<U1>::from_num(2).sqrt(), FixedU16::<U1>::SQRT_2);
|
||||
assert_eq!(FixedU16::<U7>::from_num(2).sqrt(), FixedU16::<U7>::SQRT_2);
|
||||
assert_eq!(FixedU16::<U8>::from_num(2).sqrt(), FixedU16::<U8>::SQRT_2);
|
||||
assert_eq!(FixedU16::<U9>::from_num(2).sqrt(), FixedU16::<U9>::SQRT_2);
|
||||
assert_eq!(FixedU16::<U13>::from_num(2).sqrt(), FixedU16::<U13>::SQRT_2);
|
||||
assert_eq!(FixedU16::<U14>::from_num(2).sqrt(), FixedU16::<U14>::SQRT_2);
|
||||
assert!(
|
||||
FixedU16::<U15>::MAX.sqrt() == FixedU16::<U15>::SQRT_2 - FixedU16::<U15>::DELTA
|
||||
|| FixedU16::<U15>::MAX.sqrt() == FixedU16::<U15>::SQRT_2
|
||||
);
|
||||
|
||||
assert_eq!(FixedI16::<U0>::from_num(2).sqrt(), FixedI16::<U0>::SQRT_2);
|
||||
assert_eq!(FixedI16::<U1>::from_num(2).sqrt(), FixedI16::<U1>::SQRT_2);
|
||||
assert_eq!(FixedI16::<U7>::from_num(2).sqrt(), FixedI16::<U7>::SQRT_2);
|
||||
assert_eq!(FixedI16::<U8>::from_num(2).sqrt(), FixedI16::<U8>::SQRT_2);
|
||||
assert_eq!(FixedI16::<U9>::from_num(2).sqrt(), FixedI16::<U9>::SQRT_2);
|
||||
assert_eq!(FixedI16::<U13>::from_num(2).sqrt(), FixedI16::<U13>::SQRT_2);
|
||||
assert!(
|
||||
FixedI16::<U14>::MAX.sqrt() == FixedI16::<U14>::SQRT_2 - FixedI16::<U14>::DELTA
|
||||
|| FixedI16::<U14>::MAX.sqrt() == FixedI16::<U14>::SQRT_2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_two_32() {
|
||||
assert_eq!(FixedU32::<U0>::from_num(2).sqrt(), FixedU32::<U0>::SQRT_2);
|
||||
assert_eq!(FixedU32::<U1>::from_num(2).sqrt(), FixedU32::<U1>::SQRT_2);
|
||||
assert_eq!(FixedU32::<U15>::from_num(2).sqrt(), FixedU32::<U15>::SQRT_2);
|
||||
assert_eq!(FixedU32::<U16>::from_num(2).sqrt(), FixedU32::<U16>::SQRT_2);
|
||||
assert_eq!(FixedU32::<U17>::from_num(2).sqrt(), FixedU32::<U17>::SQRT_2);
|
||||
assert_eq!(FixedU32::<U29>::from_num(2).sqrt(), FixedU32::<U29>::SQRT_2);
|
||||
assert_eq!(FixedU32::<U30>::from_num(2).sqrt(), FixedU32::<U30>::SQRT_2);
|
||||
assert!(
|
||||
FixedU32::<U31>::MAX.sqrt() == FixedU32::<U31>::SQRT_2 - FixedU32::<U31>::DELTA
|
||||
|| FixedU32::<U31>::MAX.sqrt() == FixedU32::<U31>::SQRT_2
|
||||
);
|
||||
|
||||
assert_eq!(FixedI32::<U0>::from_num(2).sqrt(), FixedI32::<U0>::SQRT_2);
|
||||
assert_eq!(FixedI32::<U1>::from_num(2).sqrt(), FixedI32::<U1>::SQRT_2);
|
||||
assert_eq!(FixedI32::<U15>::from_num(2).sqrt(), FixedI32::<U15>::SQRT_2);
|
||||
assert_eq!(FixedI32::<U16>::from_num(2).sqrt(), FixedI32::<U16>::SQRT_2);
|
||||
assert_eq!(FixedI32::<U17>::from_num(2).sqrt(), FixedI32::<U17>::SQRT_2);
|
||||
assert_eq!(FixedI32::<U29>::from_num(2).sqrt(), FixedI32::<U29>::SQRT_2);
|
||||
assert!(
|
||||
FixedI32::<U30>::MAX.sqrt() == FixedI32::<U30>::SQRT_2 - FixedI32::<U30>::DELTA
|
||||
|| FixedI32::<U30>::MAX.sqrt() == FixedI32::<U30>::SQRT_2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_two_64() {
|
||||
assert_eq!(FixedU64::<U0>::from_num(2).sqrt(), FixedU64::<U0>::SQRT_2);
|
||||
assert_eq!(FixedU64::<U1>::from_num(2).sqrt(), FixedU64::<U1>::SQRT_2);
|
||||
assert_eq!(FixedU64::<U31>::from_num(2).sqrt(), FixedU64::<U31>::SQRT_2);
|
||||
assert_eq!(FixedU64::<U32>::from_num(2).sqrt(), FixedU64::<U32>::SQRT_2);
|
||||
assert_eq!(FixedU64::<U33>::from_num(2).sqrt(), FixedU64::<U33>::SQRT_2);
|
||||
assert_eq!(FixedU64::<U61>::from_num(2).sqrt(), FixedU64::<U61>::SQRT_2);
|
||||
assert_eq!(FixedU64::<U62>::from_num(2).sqrt(), FixedU64::<U62>::SQRT_2);
|
||||
assert!(
|
||||
FixedU64::<U63>::MAX.sqrt() == FixedU64::<U63>::SQRT_2 - FixedU64::<U63>::DELTA
|
||||
|| FixedU64::<U63>::MAX.sqrt() == FixedU64::<U63>::SQRT_2
|
||||
);
|
||||
|
||||
assert_eq!(FixedI64::<U0>::from_num(2).sqrt(), FixedI64::<U0>::SQRT_2);
|
||||
assert_eq!(FixedI64::<U1>::from_num(2).sqrt(), FixedI64::<U1>::SQRT_2);
|
||||
assert_eq!(FixedI64::<U31>::from_num(2).sqrt(), FixedI64::<U31>::SQRT_2);
|
||||
assert_eq!(FixedI64::<U32>::from_num(2).sqrt(), FixedI64::<U32>::SQRT_2);
|
||||
assert_eq!(FixedI64::<U33>::from_num(2).sqrt(), FixedI64::<U33>::SQRT_2);
|
||||
assert_eq!(FixedI64::<U61>::from_num(2).sqrt(), FixedI64::<U61>::SQRT_2);
|
||||
assert!(
|
||||
FixedI64::<U62>::MAX.sqrt() == FixedI64::<U62>::SQRT_2 - FixedI64::<U62>::DELTA
|
||||
|| FixedI64::<U62>::MAX.sqrt() == FixedI64::<U62>::SQRT_2
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_two_128() {
|
||||
assert_eq!(FixedU128::<U0>::from_num(2).sqrt(), FixedU128::<U0>::SQRT_2);
|
||||
assert_eq!(FixedU128::<U1>::from_num(2).sqrt(), FixedU128::<U1>::SQRT_2);
|
||||
assert_eq!(
|
||||
FixedU128::<U63>::from_num(2).sqrt(),
|
||||
FixedU128::<U63>::SQRT_2
|
||||
);
|
||||
assert_eq!(
|
||||
FixedU128::<U64>::from_num(2).sqrt(),
|
||||
FixedU128::<U64>::SQRT_2
|
||||
);
|
||||
assert_eq!(
|
||||
FixedU128::<U65>::from_num(2).sqrt(),
|
||||
FixedU128::<U65>::SQRT_2
|
||||
);
|
||||
assert_eq!(
|
||||
FixedU128::<U125>::from_num(2).sqrt(),
|
||||
FixedU128::<U125>::SQRT_2
|
||||
);
|
||||
assert_eq!(
|
||||
FixedU128::<U126>::from_num(2).sqrt(),
|
||||
FixedU128::<U126>::SQRT_2
|
||||
);
|
||||
assert!(
|
||||
FixedU128::<U127>::MAX.sqrt() == FixedU128::<U127>::SQRT_2 - FixedU128::<U127>::DELTA
|
||||
|| FixedU128::<U127>::MAX.sqrt() == FixedU128::<U127>::SQRT_2
|
||||
);
|
||||
|
||||
assert_eq!(FixedI128::<U0>::from_num(2).sqrt(), FixedI128::<U0>::SQRT_2);
|
||||
assert_eq!(FixedI128::<U1>::from_num(2).sqrt(), FixedI128::<U1>::SQRT_2);
|
||||
assert_eq!(
|
||||
FixedI128::<U63>::from_num(2).sqrt(),
|
||||
FixedI128::<U63>::SQRT_2
|
||||
);
|
||||
assert_eq!(
|
||||
FixedI128::<U64>::from_num(2).sqrt(),
|
||||
FixedI128::<U64>::SQRT_2
|
||||
);
|
||||
assert_eq!(
|
||||
FixedI128::<U65>::from_num(2).sqrt(),
|
||||
FixedI128::<U65>::SQRT_2
|
||||
);
|
||||
assert_eq!(
|
||||
FixedI128::<U125>::from_num(2).sqrt(),
|
||||
FixedI128::<U125>::SQRT_2
|
||||
);
|
||||
assert!(
|
||||
FixedI128::<U126>::MAX.sqrt() == FixedI128::<U126>::SQRT_2 - FixedI128::<U126>::DELTA
|
||||
|| FixedI128::<U126>::MAX.sqrt() == FixedI128::<U126>::SQRT_2
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+4926
File diff suppressed because it is too large.
Load diff
Vendored
+322
@@ -0,0 +1,322 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
use crate::traits::FixedEquiv;
|
||||
#[cfg(feature = "arbitrary")]
|
||||
use arbitrary::Arbitrary;
|
||||
use az::{
|
||||
Cast, CastFrom, CheckedCast, CheckedCastFrom, OverflowingCast, OverflowingCastFrom,
|
||||
SaturatingCast, SaturatingCastFrom, UnwrappedCast, UnwrappedCastFrom, WrappingCast,
|
||||
WrappingCastFrom,
|
||||
};
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use bytemuck::{Contiguous, Pod};
|
||||
use core::fmt::{Binary, Debug, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex};
|
||||
use core::hash::Hash;
|
||||
use core::iter::{Product, Sum};
|
||||
use core::num::ParseIntError;
|
||||
use core::ops::{
|
||||
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
|
||||
Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
|
||||
};
|
||||
use core::str::FromStr;
|
||||
#[cfg(feature = "num-traits")]
|
||||
use num_traits::{
|
||||
Num, NumAssignRef, NumRef,
|
||||
cast::{AsPrimitive, FromPrimitive},
|
||||
int::PrimInt,
|
||||
ops::{
|
||||
checked::{CheckedNeg, CheckedRem, CheckedShl, CheckedShr},
|
||||
euclid::{CheckedEuclid, Euclid},
|
||||
mul_add::{MulAdd, MulAddAssign},
|
||||
overflowing::{OverflowingAdd, OverflowingMul, OverflowingSub},
|
||||
saturating::{SaturatingAdd, SaturatingMul, SaturatingSub},
|
||||
wrapping::{WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub},
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{de::Deserialize, ser::Serialize};
|
||||
|
||||
macro_rules! impl_bits {
|
||||
($Bits:ident) => {
|
||||
impl FixedBits for $Bits {
|
||||
const MIN: $Bits = $Bits::MIN;
|
||||
const MAX: $Bits = $Bits::MAX;
|
||||
const IS_SIGNED: bool = $Bits::MIN != 0;
|
||||
const BITS: u32 = $Bits::BITS;
|
||||
}
|
||||
impl Sealed for $Bits {}
|
||||
impl FixedBitsCast<i8> for $Bits {}
|
||||
impl FixedBitsCast<i16> for $Bits {}
|
||||
impl FixedBitsCast<i32> for $Bits {}
|
||||
impl FixedBitsCast<i64> for $Bits {}
|
||||
impl FixedBitsCast<i128> for $Bits {}
|
||||
impl FixedBitsCast<isize> for $Bits {}
|
||||
impl FixedBitsCast<u8> for $Bits {}
|
||||
impl FixedBitsCast<u16> for $Bits {}
|
||||
impl FixedBitsCast<u32> for $Bits {}
|
||||
impl FixedBitsCast<u64> for $Bits {}
|
||||
impl FixedBitsCast<u128> for $Bits {}
|
||||
impl FixedBitsCast<usize> for $Bits {}
|
||||
impl FixedBitsOptionalArbitrary for $Bits {}
|
||||
impl FixedBitsOptionalBorsh for $Bits {}
|
||||
impl FixedBitsOptionalNum for $Bits {}
|
||||
impl FixedBitsOptionalSerde for $Bits {}
|
||||
};
|
||||
}
|
||||
|
||||
/// This trait is implemented for <code>[Fixed]::[Bits]</code> for all
|
||||
/// fixed-point numbers.
|
||||
///
|
||||
/// This provides some facilities to manipulate bits in generic functions.
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented for more types; it is
|
||||
/// implemented for [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], [`u8`],
|
||||
/// [`u16`], [`u32`], [`u64`], and [`u128`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use az::OverflowingAs;
|
||||
/// use fixed::traits::Fixed;
|
||||
/// use fixed::types::*;
|
||||
/// fn limited_positive_bits<F: Fixed>(fixed: F) -> Option<u32> {
|
||||
/// let bits = fixed.to_bits();
|
||||
/// match bits.overflowing_as::<u32>() {
|
||||
/// (wrapped, false) => Some(wrapped),
|
||||
/// (_, true) => None,
|
||||
/// }
|
||||
/// }
|
||||
/// assert_eq!(limited_positive_bits(I16F16::from_bits(100)), Some(100));
|
||||
/// assert_eq!(limited_positive_bits(I16F16::from_bits(-100)), None);
|
||||
/// ```
|
||||
///
|
||||
/// [Bits]: crate::traits::Fixed::Bits
|
||||
/// [Fixed]: crate::traits::Fixed
|
||||
pub trait FixedBits
|
||||
where
|
||||
Self: Default + Hash + Ord,
|
||||
Self: Contiguous + Pod,
|
||||
Self: Debug + Display + LowerExp + UpperExp,
|
||||
Self: Binary + Octal + LowerHex + UpperHex,
|
||||
Self: FromStr<Err = ParseIntError>,
|
||||
Self: Add<Output = Self> + AddAssign,
|
||||
Self: Sub<Output = Self> + SubAssign,
|
||||
Self: Mul<Output = Self> + MulAssign,
|
||||
Self: Div<Output = Self> + DivAssign,
|
||||
Self: Rem<Output = Self> + RemAssign,
|
||||
Self: Not<Output = Self>,
|
||||
Self: BitAnd<Output = Self> + BitAndAssign,
|
||||
Self: BitOr<Output = Self> + BitOrAssign,
|
||||
Self: BitXor<Output = Self> + BitXorAssign,
|
||||
Self: Shl<u32, Output = Self> + ShlAssign<u32>,
|
||||
Self: Shr<u32, Output = Self> + ShrAssign<u32>,
|
||||
Self: Sum + Product,
|
||||
Self: FixedEquiv,
|
||||
Self: FixedBitsCast<i8> + FixedBitsCast<i16> + FixedBitsCast<i32>,
|
||||
Self: FixedBitsCast<i64> + FixedBitsCast<i128> + FixedBitsCast<isize>,
|
||||
Self: FixedBitsCast<u8> + FixedBitsCast<u16> + FixedBitsCast<u32>,
|
||||
Self: FixedBitsCast<u64> + FixedBitsCast<u128> + FixedBitsCast<usize>,
|
||||
Self: FixedBitsOptionalArbitrary,
|
||||
Self: FixedBitsOptionalBorsh,
|
||||
Self: FixedBitsOptionalNum,
|
||||
Self: FixedBitsOptionalSerde,
|
||||
Self: Sealed,
|
||||
{
|
||||
/// The smallest value that can be represented by this integer type.
|
||||
const MIN: Self;
|
||||
|
||||
/// The largest value that can be represented by this integer type.
|
||||
const MAX: Self;
|
||||
|
||||
/// [`true`] if this integer type is signed.
|
||||
const IS_SIGNED: bool;
|
||||
|
||||
/// The size of this integer type in bits.
|
||||
const BITS: u32;
|
||||
}
|
||||
|
||||
/// This trait is used to provide supertraits to the [`FixedBits`] trait, and
|
||||
/// should not be used directly.
|
||||
pub trait FixedBitsCast<Prim>
|
||||
where
|
||||
Self: TryInto<Prim> + TryFrom<Prim>,
|
||||
Self: Cast<Prim> + CastFrom<Prim>,
|
||||
Self: CheckedCast<Prim> + CheckedCastFrom<Prim>,
|
||||
Self: SaturatingCast<Prim> + SaturatingCastFrom<Prim>,
|
||||
Self: WrappingCast<Prim> + WrappingCastFrom<Prim>,
|
||||
Self: UnwrappedCast<Prim> + UnwrappedCastFrom<Prim>,
|
||||
Self: OverflowingCast<Prim> + OverflowingCastFrom<Prim>,
|
||||
Self: Sealed,
|
||||
{
|
||||
}
|
||||
|
||||
pub trait Sealed {}
|
||||
|
||||
impl_bits! { i8 }
|
||||
impl_bits! { i16 }
|
||||
impl_bits! { i32 }
|
||||
impl_bits! { i64 }
|
||||
impl_bits! { i128 }
|
||||
impl_bits! { u8 }
|
||||
impl_bits! { u16 }
|
||||
impl_bits! { u32 }
|
||||
impl_bits! { u64 }
|
||||
impl_bits! { u128 }
|
||||
|
||||
#[cfg(not(feature = "arbitrary"))]
|
||||
/// This trait is used to provide supertrait to the [`FixedBits`] trait
|
||||
/// depending on the crates’s [optional features], and should not be used
|
||||
/// directly.
|
||||
///
|
||||
/// If the `arbitrary` feature is enabled, [`Arbitrary`] is a supertrait of
|
||||
/// [`FixedBits`].
|
||||
///
|
||||
/// [optional features]: crate#optional-features
|
||||
pub trait FixedBitsOptionalArbitrary: Sealed {}
|
||||
|
||||
#[cfg(feature = "arbitrary")]
|
||||
/// This trait is used to provide supertrait to the [`FixedBits`] trait
|
||||
/// depending on the crates’s [optional features], and should not be used
|
||||
/// directly.
|
||||
///
|
||||
/// If the `arbitrary` feature is enabled, [`Arbitrary`] is a supertrait of
|
||||
/// [`FixedBits`].
|
||||
///
|
||||
/// [optional features]: crate#optional-features
|
||||
pub trait FixedBitsOptionalArbitrary: Sealed
|
||||
where
|
||||
Self: for<'a> Arbitrary<'a>,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "borsh"))]
|
||||
/// This trait is used to provide supertraits to the [`FixedBits`] trait
|
||||
/// depending on the crates’s [optional features], and should not be used
|
||||
/// directly.
|
||||
///
|
||||
/// If the `borsh` experimental feature is enabled, [`BorshSerialize`] and
|
||||
/// [`BorshDeserialize`] are supertraits of [`FixedBits`].
|
||||
///
|
||||
/// [optional features]: crate#optional-features
|
||||
pub trait FixedBitsOptionalBorsh: Sealed {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
/// This trait is used to provide supertraits to the [`FixedBits`] trait
|
||||
/// depending on the crates’s [optional features], and should not be used
|
||||
/// directly.
|
||||
///
|
||||
/// If the `borsh` experimental feature is enabled, [`BorshSerialize`] and
|
||||
/// [`BorshDeserialize`] are supertraits of [`FixedBits`].
|
||||
///
|
||||
/// [optional features]: crate#optional-features
|
||||
pub trait FixedBitsOptionalBorsh: Sealed
|
||||
where
|
||||
Self: BorshSerialize + BorshDeserialize,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "num-traits"))]
|
||||
/// This trait is used to provide supertraits to the [`FixedBits`] trait
|
||||
/// depending on the crates’s [optional features], and should not be used
|
||||
/// directly.
|
||||
///
|
||||
/// If the [`num-traits` experimental feature][experimental features] is
|
||||
/// enabled, the following are supertraits of [`FixedBits`]:
|
||||
///
|
||||
/// * [`PrimInt`], [`FromPrimitive`]
|
||||
/// * <code>[AsPrimitive][`AsPrimitive`]<T></code> where `T` can be [`i8`],
|
||||
/// [`i16`], [`i32`], [`i64`], [`i128`], [`isize`], [`u8`], [`u16`],
|
||||
/// [`u32`], [`u64`], [`u128`], [`usize`], [`f32`] or [`f64`]
|
||||
/// * [`CheckedNeg`], [`CheckedRem`], [`CheckedShl`], [`CheckedShr`]
|
||||
/// * [`SaturatingAdd`], [`SaturatingSub`], [`SaturatingMul`]
|
||||
/// * [`WrappingAdd`], [`WrappingSub`], [`WrappingNeg`], [`WrappingMul`],
|
||||
/// [`WrappingShl`], [`WrappingShr`]
|
||||
/// * [`OverflowingAdd`], [`OverflowingSub`], [`OverflowingMul`]
|
||||
/// * [`Euclid`], [`CheckedEuclid`]
|
||||
/// * [`MulAdd`], [`MulAddAssign`]
|
||||
///
|
||||
/// [experimental features]: crate#experimental-optional-features
|
||||
/// [optional features]: crate#optional-features
|
||||
pub trait FixedBitsOptionalNum: Sealed {}
|
||||
|
||||
#[cfg(feature = "num-traits")]
|
||||
/// This trait is used to provide supertraits to the [`FixedBits`] trait
|
||||
/// depending on the crates’s [optional features], and should not be used
|
||||
/// directly.
|
||||
///
|
||||
/// If the [`num-traits` experimental feature][experimental features] is
|
||||
/// enabled, the following are supertraits of [`FixedBits`]:
|
||||
///
|
||||
/// * [`Num`], [`NumRef`], [`NumAssignRef`]
|
||||
/// * [`PrimInt`], [`FromPrimitive`]
|
||||
/// * <code>[AsPrimitive][`AsPrimitive`]<T></code> where `T` can be [`i8`],
|
||||
/// [`i16`], [`i32`], [`i64`], [`i128`], [`isize`], [`u8`], [`u16`],
|
||||
/// [`u32`], [`u64`], [`u128`], [`usize`], [`f32`] or [`f64`]
|
||||
/// * [`CheckedNeg`], [`CheckedRem`], [`CheckedShl`], [`CheckedShr`]
|
||||
/// * [`SaturatingAdd`], [`SaturatingSub`], [`SaturatingMul`]
|
||||
/// * [`WrappingAdd`], [`WrappingSub`], [`WrappingNeg`], [`WrappingMul`],
|
||||
/// [`WrappingShl`], [`WrappingShr`]
|
||||
/// * [`OverflowingAdd`], [`OverflowingSub`], [`OverflowingMul`]
|
||||
/// * [`Euclid`], [`CheckedEuclid`]
|
||||
/// * [`MulAdd`], [`MulAddAssign`]
|
||||
///
|
||||
/// [experimental features]: crate#experimental-optional-features
|
||||
/// [optional features]: crate#optional-features
|
||||
pub trait FixedBitsOptionalNum: Sealed
|
||||
where
|
||||
Self: Num<FromStrRadixErr = ParseIntError> + NumRef + NumAssignRef,
|
||||
Self: PrimInt + FromPrimitive,
|
||||
Self: AsPrimitive<i8> + AsPrimitive<i16> + AsPrimitive<i32>,
|
||||
Self: AsPrimitive<i64> + AsPrimitive<i128> + AsPrimitive<isize>,
|
||||
Self: AsPrimitive<u8> + AsPrimitive<u16> + AsPrimitive<u32>,
|
||||
Self: AsPrimitive<u64> + AsPrimitive<u128> + AsPrimitive<usize>,
|
||||
Self: AsPrimitive<f32> + AsPrimitive<f64>,
|
||||
Self: CheckedNeg + CheckedRem + CheckedShl + CheckedShr,
|
||||
Self: SaturatingAdd + SaturatingSub + SaturatingMul,
|
||||
Self: WrappingAdd + WrappingSub + WrappingNeg + WrappingMul,
|
||||
Self: WrappingShl + WrappingShr,
|
||||
Self: OverflowingAdd + OverflowingSub + OverflowingMul,
|
||||
Self: Euclid + CheckedEuclid,
|
||||
Self: MulAdd + MulAddAssign,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "serde"))]
|
||||
/// This trait is used to provide supertraits to the [`FixedBits`] trait
|
||||
/// depending on the crates’s [optional features], and should not be used
|
||||
/// directly.
|
||||
///
|
||||
/// If the `serde` feature is enabled, [`Serialize`] and [`Deserialize`] are
|
||||
/// supertraits of [`FixedBits`].
|
||||
///
|
||||
/// [optional features]: crate#optional-features
|
||||
pub trait FixedBitsOptionalSerde: Sealed {}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
/// This trait is used to provide supertraits to the [`FixedBits`] trait
|
||||
/// depending on the crates’s [optional features], and should not be used
|
||||
/// directly.
|
||||
///
|
||||
/// If the `serde` feature is enabled, [`Serialize`] and [`Deserialize`] are
|
||||
/// supertraits of [`FixedBits`].
|
||||
///
|
||||
/// [optional features]: crate#optional-features
|
||||
pub trait FixedBitsOptionalSerde: Sealed
|
||||
where
|
||||
Self: Serialize + for<'de> Deserialize<'de>,
|
||||
{
|
||||
}
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
// Copyright © 2018–2025 Trevor Spiteri
|
||||
|
||||
// This library is free software: you can redistribute it and/or
|
||||
// modify it under the terms of either
|
||||
//
|
||||
// * the Apache License, Version 2.0 or
|
||||
// * the MIT License
|
||||
//
|
||||
// at your option.
|
||||
//
|
||||
// You should have recieved copies of the Apache License and the MIT
|
||||
// License along with the library. If not, see
|
||||
// <https://www.apache.org/licenses/LICENSE-2.0> and
|
||||
// <https://opensource.org/licenses/MIT>.
|
||||
|
||||
/*!
|
||||
Extra types that do not need to be handled directly.
|
||||
|
||||
These types are mostly reexports from the [*typenum* crate].
|
||||
|
||||
[*typenum* crate]: https://crates.io/crates/typenum
|
||||
*/
|
||||
|
||||
pub use typenum::{
|
||||
Diff, IsLessOrEqual, Sum, True, U0, U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13,
|
||||
U14, U15, U16, U17, U18, U19, U20, U21, U22, U23, U24, U25, U26, U27, U28, U29, U30, U31, U32,
|
||||
U33, U34, U35, U36, U37, U38, U39, U40, U41, U42, U43, U44, U45, U46, U47, U48, U49, U50, U51,
|
||||
U52, U53, U54, U55, U56, U57, U58, U59, U60, U61, U62, U63, U64, U65, U66, U67, U68, U69, U70,
|
||||
U71, U72, U73, U74, U75, U76, U77, U78, U79, U80, U81, U82, U83, U84, U85, U86, U87, U88, U89,
|
||||
U90, U91, U92, U93, U94, U95, U96, U97, U98, U99, U100, U101, U102, U103, U104, U105, U106,
|
||||
U107, U108, U109, U110, U111, U112, U113, U114, U115, U116, U117, U118, U119, U120, U121, U122,
|
||||
U123, U124, U125, U126, U127, U128, Unsigned,
|
||||
};
|
||||
|
||||
/// Implemented for all [`Unsigned`] integers ≤ 8.
|
||||
pub trait LeEqU8: Unsigned + IsLessOrEqual<U8, Output = True> {}
|
||||
impl<T: Unsigned + IsLessOrEqual<U8, Output = True>> LeEqU8 for T {}
|
||||
/// Implemented for all [`Unsigned`] integers ≤ 16.
|
||||
pub trait LeEqU16: Unsigned + IsLessOrEqual<U16, Output = True> {}
|
||||
impl<T: Unsigned + IsLessOrEqual<U16, Output = True>> LeEqU16 for T {}
|
||||
/// Implemented for all [`Unsigned`] integers ≤ 32.
|
||||
pub trait LeEqU32: Unsigned + IsLessOrEqual<U32, Output = True> {}
|
||||
impl<T: Unsigned + IsLessOrEqual<U32, Output = True>> LeEqU32 for T {}
|
||||
/// Implemented for all [`Unsigned`] integers ≤ 64.
|
||||
pub trait LeEqU64: Unsigned + IsLessOrEqual<U64, Output = True> {}
|
||||
impl<T: Unsigned + IsLessOrEqual<U64, Output = True>> LeEqU64 for T {}
|
||||
/// Implemented for all [`Unsigned`] integers ≤ 128.
|
||||
pub trait LeEqU128: Unsigned + IsLessOrEqual<U128, Output = True> {}
|
||||
impl<T: Unsigned + IsLessOrEqual<U128, Output = True>> LeEqU128 for T {}
|
||||
Vendored
+1084
File diff suppressed because it is too large.
Load diff
Vendored
+2833
File diff suppressed because it is too large.
Load diff
Vendored
+2367
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user