This repository has been archived on 2026-08-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
donglora/vendor/fixed/src/f128.rs
T
swaitsandClaude Opus 4.6 3102fc747a 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>
2026-03-29 20:42:54 -06:00

1266 lines
42 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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>.
//! Constants specific to the [`F128`] quadruple-precision floating-point type.
//!
//! Mathematically significant numbers are provided in the [`consts`] sub-module.
//!
//! For constants related to the floating-point representation itself, see the
//! associated constants defined directly on the [`F128`] type.
//!
//! # Planned deprecation
//!
//! This module will be deprecated when the [`f128` feature] is ready and
//! stabilized.
//!
//! [`f128` feature]: https://github.com/rust-lang/rust/issues/116909
use crate::F128;
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::num::FpCategory;
use core::ops::Neg;
use half::{bf16 as half_bf16, f16 as half_f16};
const PREC: u32 = 113;
const EXP_BITS: u32 = u128::BITS - PREC;
const EXP_BIAS: u32 = (1 << (EXP_BITS - 1)) - 1;
const SIGN_MASK: u128 = 1 << (u128::BITS - 1);
const EXP_MASK: u128 = ((1 << EXP_BITS) - 1) << (PREC - 1);
const MANT_MASK: u128 = (1 << (PREC - 1)) - 1;
pub(crate) mod private {
/// A *binary128* floating-point number (`f128`).
///
/// This type can be used to
///
/// * convert between fixed-point numbers and the bit representation of
/// 128-bit floating-point numbers.
/// * compare fixed-point numbers and the bit representation of 128-bit
/// floating-point numbers.
///
/// This type does *not* support arithmetic or general analytic functions.
///
/// Please see [<i>Quadruple-precision floating-point format</i> on
/// Wikipedia][quad] for more information on *binary128*.
///
/// *See also the <code>[fixed]::[f128]::[consts]</code> module.*
///
/// # Planned deprecation
///
/// This struct will be deprecated when the [`f128` feature] is ready and
/// stabilized.
///
/// # Examples
///
/// ```rust
/// use fixed::types::I16F16;
/// use fixed::F128;
/// assert_eq!(I16F16::ONE.to_num::<F128>(), F128::ONE);
/// assert_eq!(I16F16::from_num(F128::ONE), I16F16::ONE);
///
/// // fixed-point numbers can be compared directly to F128 values
/// assert!(I16F16::from_num(1.5) > F128::ONE);
/// assert!(I16F16::from_num(0.5) < F128::ONE);
/// ```
///
/// [`f128` feature]: https://github.com/rust-lang/rust/issues/116909
/// [consts]: crate::f128::consts
/// [f128]: crate::f128
/// [fixed]: crate
/// [quad]: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format
#[derive(Clone, Copy, Default, Debug)]
pub struct F128 {
pub(crate) bits: u128,
}
}
impl F128 {
/// Zero.
pub const ZERO: F128 = F128::from_bits(0);
/// Negative zero (&minus;0).
pub const NEG_ZERO: F128 = F128::from_bits(SIGN_MASK);
/// One.
pub const ONE: F128 = F128::from_bits((EXP_BIAS as u128) << (PREC - 1));
/// Negative one (&minus;1).
pub const NEG_ONE: F128 = F128::from_bits(SIGN_MASK | F128::ONE.to_bits());
/// Smallest positive subnormal number.
///
/// Equal to 2<sup>[`MIN_EXP`]&nbsp;&minus;&nbsp;[`MANTISSA_DIGITS`]</sup>.
///
/// [`MANTISSA_DIGITS`]: Self::MANTISSA_DIGITS
/// [`MIN_EXP`]: Self::MIN_EXP
pub const MIN_POSITIVE_SUB: F128 = F128::from_bits(1);
/// Smallest positive normal number.
///
/// Equal to 2<sup>[`MIN_EXP`]&nbsp;&minus;&nbsp;1</sup>.
///
/// [`MIN_EXP`]: Self::MIN_EXP
pub const MIN_POSITIVE: F128 = F128::from_bits(MANT_MASK + 1);
/// Largest finite number.
///
/// Equal to
/// (1&nbsp;&minus;&nbsp;2<sup>&minus;[`MANTISSA_DIGITS`]</sup>)&nbsp;2<sup>[`MAX_EXP`]</sup>.
///
/// [`MANTISSA_DIGITS`]: Self::MANTISSA_DIGITS
/// [`MAX_EXP`]: Self::MAX_EXP
pub const MAX: F128 = F128::from_bits(EXP_MASK - 1);
/// Smallest finite number (&minus;[`MAX`]).
///
/// [`MAX`]: Self::MAX
pub const MIN: F128 = F128::from_bits(SIGN_MASK | F128::MAX.to_bits());
/// Infinity (∞).
pub const INFINITY: F128 = F128::from_bits(EXP_MASK);
/// Negative infinity (&minus;∞).
pub const NEG_INFINITY: F128 = F128::from_bits(SIGN_MASK | EXP_MASK);
/// NaN.
pub const NAN: F128 = F128::from_bits(EXP_MASK | (1u128 << (PREC - 2)));
/// The radix or base of the internal representation (2).
pub const RADIX: u32 = 2;
/// Number of significant digits in base 2.
pub const MANTISSA_DIGITS: u32 = PREC;
/// Maximum <i>x</i> such that any decimal number with <i>x</i> significant
/// digits can be converted to [`F128`] and back without loss.
///
/// Equal to
/// floor(log<sub>10</sub>&nbsp;2<sup>[`MANTISSA_DIGITS`]&nbsp;&minus;&nbsp;1</sup>).
///
/// [`MANTISSA_DIGITS`]: Self::MANTISSA_DIGITS
pub const DIGITS: u32 = 33;
/// The difference between 1 and the next larger representable number.
///
/// Equal to 2<sup>1&nbsp;&minus;&nbsp;[`MANTISSA_DIGITS`]</sup>.
///
/// [`MANTISSA_DIGITS`]: Self::MANTISSA_DIGITS
pub const EPSILON: F128 = F128::from_bits(((EXP_BIAS - (PREC - 1)) as u128) << (PREC - 1));
/// If <i>x</i>&nbsp;=&nbsp;`MIN_EXP`, then normal numbers
/// ≥&nbsp;0.5&nbsp;×&nbsp;2<sup><i>x</i></sup>.
pub const MIN_EXP: i32 = 3 - F128::MAX_EXP;
/// If <i>x</i>&nbsp;=&nbsp;`MAX_EXP`, then normal numbers
/// <&nbsp;1&nbsp;×&nbsp;2<sup><i>x</i></sup>.
pub const MAX_EXP: i32 = EXP_BIAS as i32 + 1;
/// Minimum <i>x</i> for which 10<sup><i>x</i></sup> is in the normal range
/// of [`F128`].
///
/// Equal to ceil(log<sub>10</sub>&nbsp;[`MIN_POSITIVE`]).
///
/// [`MIN_POSITIVE`]: Self::MIN_POSITIVE
pub const MIN_10_EXP: i32 = -4931;
/// Maximum <i>x</i> for which 10<sup><i>x</i></sup> is in the normal range
/// of [`F128`].
///
/// Equal to floor(log<sub>10</sub>&nbsp;[`MAX`]).
///
/// [`MAX`]: Self::MAX
pub const MAX_10_EXP: i32 = 4932;
/// Raw transmutation from [`u128`].
///
/// # Examples
///
/// ```rust
/// use fixed::F128;
/// let infinity_bits = 0x7FFF_u128 << 112;
/// assert!(F128::from_bits(infinity_bits - 1).is_finite());
/// assert!(!F128::from_bits(infinity_bits).is_finite());
/// ```
#[inline]
#[must_use]
pub const fn from_bits(bits: u128) -> F128 {
F128 { bits }
}
/// Raw transmutation to [`u128`].
///
/// # Examples
///
/// ```rust
/// use fixed::F128;
/// assert_eq!(F128::ONE.to_bits(), 0x3FFF_u128 << 112);
/// assert_ne!(F128::ONE.to_bits(), 1u128);
/// ```
#[inline]
#[must_use]
pub const fn to_bits(self) -> u128 {
self.bits
}
/// Creates a number from a byte array in big-endian byte order.
#[inline]
#[must_use]
pub const fn from_be_bytes(bytes: [u8; 16]) -> F128 {
F128::from_bits(u128::from_be_bytes(bytes))
}
/// Creates a number from a byte array in little-endian byte order.
#[inline]
#[must_use]
pub const fn from_le_bytes(bytes: [u8; 16]) -> F128 {
F128::from_bits(u128::from_le_bytes(bytes))
}
/// Creates a number from a byte array in native-endian byte order.
#[inline]
#[must_use]
pub const fn from_ne_bytes(bytes: [u8; 16]) -> F128 {
F128::from_bits(u128::from_ne_bytes(bytes))
}
/// Returns the memory representation of the number as a byte array in
/// big-endian byte order.
#[inline]
#[must_use]
pub const fn to_be_bytes(self) -> [u8; 16] {
self.to_bits().to_be_bytes()
}
/// Returns the memory representation of the number as a byte array in
/// little-endian byte order.
#[inline]
#[must_use]
pub const fn to_le_bytes(self) -> [u8; 16] {
self.to_bits().to_le_bytes()
}
/// Returns the memory representation of the number as a byte array in
/// native-endian byte order.
#[inline]
#[must_use]
pub const fn to_ne_bytes(self) -> [u8; 16] {
self.to_bits().to_ne_bytes()
}
/// Returns [`true`] if the number is NaN.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert!(F128::NAN.is_nan());
///
/// assert!(!F128::ONE.is_nan());
/// assert!(!F128::INFINITY.is_nan());
/// assert!(!F128::NEG_INFINITY.is_nan());
/// ```
#[inline]
#[must_use]
pub const fn is_nan(self) -> bool {
(self.to_bits() & !SIGN_MASK) > EXP_MASK
}
/// Returns [`true`] if the number is infinite.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert!(F128::INFINITY.is_infinite());
/// assert!(F128::NEG_INFINITY.is_infinite());
///
/// assert!(!F128::ONE.is_infinite());
/// assert!(!F128::NAN.is_infinite());
/// ```
#[inline]
#[must_use]
pub const fn is_infinite(self) -> bool {
(self.to_bits() & !SIGN_MASK) == EXP_MASK
}
/// Returns [`true`] if the number is neither infinite nor NaN.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert!(F128::ONE.is_finite());
/// assert!(F128::MAX.is_finite());
///
/// assert!(!F128::INFINITY.is_finite());
/// assert!(!F128::NEG_INFINITY.is_finite());
/// assert!(!F128::NAN.is_finite());
/// ```
#[inline]
#[must_use]
pub const fn is_finite(self) -> bool {
(self.to_bits() & EXP_MASK) != EXP_MASK
}
/// Returns [`true`] if the number is zero.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert!(F128::ZERO.is_zero());
/// assert!(F128::NEG_ZERO.is_zero());
///
/// assert!(!F128::MIN_POSITIVE_SUB.is_zero());
/// assert!(!F128::NAN.is_zero());
/// ```
#[inline]
#[must_use]
pub const fn is_zero(self) -> bool {
(self.to_bits() & !SIGN_MASK) == 0
}
/// Returns [`true`] if the number is subnormal.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert!(F128::MIN_POSITIVE_SUB.is_subnormal());
///
/// assert!(!F128::ZERO.is_subnormal());
/// assert!(!F128::MIN_POSITIVE.is_subnormal());
/// ```
#[inline]
#[must_use]
pub const fn is_subnormal(self) -> bool {
let abs = self.to_bits() & !SIGN_MASK;
0 < abs && abs < F128::MIN_POSITIVE.to_bits()
}
/// Returns [`true`] if the number is neither zero, infinite, subnormal, or NaN.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert!(F128::MIN.is_normal());
/// assert!(F128::MIN_POSITIVE.is_normal());
/// assert!(F128::MAX.is_normal());
///
/// assert!(!F128::ZERO.is_normal());
/// assert!(!F128::MIN_POSITIVE_SUB.is_normal());
/// assert!(!F128::INFINITY.is_normal());
/// assert!(!F128::NAN.is_normal());
/// ```
#[inline]
#[must_use]
pub const fn is_normal(self) -> bool {
let abs = self.to_bits() & !SIGN_MASK;
F128::MIN_POSITIVE.to_bits() <= abs && abs <= F128::MAX.to_bits()
}
/// Returns the floating point category of the number.
///
/// If only one property is going to be tested, it is generally faster to
/// use the specific predicate instead.
///
/// # Example
///
/// ```rust
/// use core::num::FpCategory;
/// use fixed::F128;
///
/// assert_eq!(F128::ZERO.classify(), FpCategory::Zero);
/// assert_eq!(F128::MIN_POSITIVE_SUB.classify(), FpCategory::Subnormal);
/// assert_eq!(F128::MIN_POSITIVE.classify(), FpCategory::Normal);
/// assert_eq!(F128::INFINITY.classify(), FpCategory::Infinite);
/// assert_eq!(F128::NAN.classify(), FpCategory::Nan);
/// ```
#[inline]
#[must_use]
pub const fn classify(self) -> FpCategory {
let exp = self.to_bits() & EXP_MASK;
let mant = self.to_bits() & MANT_MASK;
if exp == 0 {
if mant == 0 {
FpCategory::Zero
} else {
FpCategory::Subnormal
}
} else if exp == EXP_MASK {
if mant == 0 {
FpCategory::Infinite
} else {
FpCategory::Nan
}
} else {
FpCategory::Normal
}
}
/// Returns the absolute value of the number.
///
/// The only difference possible between the input value and the returned
/// value is in the sign bit, which is always cleared in the return value.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// // -0 == +0, but -0 bits != +0 bits
/// assert_eq!(F128::NEG_ZERO, F128::ZERO);
/// assert_ne!(F128::NEG_ZERO.to_bits(), F128::ZERO.to_bits());
/// assert_eq!(F128::NEG_ZERO.abs().to_bits(), F128::ZERO.to_bits());
///
/// assert_eq!(F128::NEG_INFINITY.abs(), F128::INFINITY);
/// assert_eq!(F128::MIN.abs(), F128::MAX);
///
/// assert!(F128::NAN.abs().is_nan());
/// ```
#[inline]
#[must_use]
pub const fn abs(self) -> F128 {
F128::from_bits(self.to_bits() & !SIGN_MASK)
}
/// Returns a number that represents the sign of the input value.
///
/// * 1 if the number is positive, +0, or +∞
/// * &minus;1 if the number is negative, &minus;0, or &minus;∞
/// * NaN if the number is NaN
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert_eq!(F128::ONE.signum(), F128::ONE);
/// assert_eq!(F128::INFINITY.signum(), F128::ONE);
/// assert_eq!(F128::NEG_ZERO.signum(), F128::NEG_ONE);
/// assert_eq!(F128::MIN.signum(), F128::NEG_ONE);
///
/// assert!(F128::NAN.signum().is_nan());
/// ```
#[inline]
#[must_use]
pub const fn signum(self) -> F128 {
if self.is_nan() {
self
} else if self.is_sign_positive() {
F128::ONE
} else {
F128::NEG_ONE
}
}
/// Returns a number composed of the magnitude of `self` and the sign of `sign`.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert_eq!(F128::ONE.copysign(F128::NEG_ZERO), F128::NEG_ONE);
/// assert_eq!(F128::ONE.copysign(F128::ZERO), F128::ONE);
/// assert_eq!(F128::NEG_ONE.copysign(F128::NEG_INFINITY), F128::NEG_ONE);
/// assert_eq!(F128::NEG_ONE.copysign(F128::INFINITY), F128::ONE);
///
/// assert!(F128::NAN.copysign(F128::ONE).is_nan());
/// assert!(F128::NAN.copysign(F128::ONE).is_sign_positive());
/// assert!(F128::NAN.copysign(F128::NEG_ONE).is_sign_negative());
/// ```
#[inline]
#[must_use]
pub const fn copysign(self, sign: F128) -> F128 {
F128::from_bits((self.to_bits() & !SIGN_MASK) | (sign.to_bits() & SIGN_MASK))
}
/// Returns [`true`] if the number has a positive sign, including +0, +∞,
/// and NaN without a negative sign bit.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert!(F128::ZERO.is_sign_positive());
/// assert!(F128::MAX.is_sign_positive());
/// assert!(F128::INFINITY.is_sign_positive());
///
/// assert!(!F128::NEG_ZERO.is_sign_positive());
/// assert!(!F128::MIN.is_sign_positive());
/// assert!(!F128::NEG_INFINITY.is_sign_positive());
/// ```
#[inline]
#[must_use]
pub const fn is_sign_positive(self) -> bool {
(self.to_bits() & SIGN_MASK) == 0
}
/// Returns [`true`] if the number has a negative sign, including &minus;0,
/// &minus;∞, and NaN with a negative sign bit.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert!(F128::NEG_ZERO.is_sign_negative());
/// assert!(F128::MIN.is_sign_negative());
/// assert!(F128::NEG_INFINITY.is_sign_negative());
///
/// assert!(!F128::ZERO.is_sign_negative());
/// assert!(!F128::MAX.is_sign_negative());
/// assert!(!F128::INFINITY.is_sign_negative());
/// ```
#[inline]
#[must_use]
pub const fn is_sign_negative(self) -> bool {
(self.to_bits() & SIGN_MASK) != 0
}
/// Returns the maximum of two numbers, ignoring NaN.
///
/// If one of the arguments is NaN, then the other argument is returned.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert_eq!(F128::ZERO.max(F128::ONE), F128::ONE);
/// ```
#[inline]
#[must_use]
pub const fn max(self, other: F128) -> F128 {
if self.is_nan() || matches!(partial_cmp(&self, &other), Some(Ordering::Less)) {
other
} else {
self
}
}
/// Returns the minimum of two numbers, ignoring NaN.
///
/// If one of the arguments is NaN, then the other argument is returned.
///
/// # Example
///
/// ```rust
/// use fixed::F128;
///
/// assert_eq!(F128::ZERO.min(F128::ONE), F128::ZERO);
/// ```
#[inline]
#[must_use]
pub const fn min(self, other: F128) -> F128 {
if self.is_nan() || matches!(partial_cmp(&self, &other), Some(Ordering::Greater)) {
other
} else {
self
}
}
/// Clamps the value within the specified bounds.
///
/// Returns `min` if `self`&nbsp;<&nbsp;`min`, `max` if
/// `self`&nbsp;>&nbsp;`max`, or `self` otherwise.
///
/// Note that this method returns NaN if the initial value is NaN.
///
/// # Panics
///
/// Panics if `min`&nbsp;>&nbsp;`max`, `min` is NaN, or `max` is NaN.
///
/// # Examples
///
/// ```
/// use fixed::F128;
/// assert_eq!(F128::MIN.clamp(F128::NEG_ONE, F128::ONE), F128::NEG_ONE);
/// assert_eq!(F128::ZERO.clamp(F128::NEG_ONE, F128::ONE), F128::ZERO);
/// assert_eq!(F128::MAX.clamp(F128::NEG_ONE, F128::ONE), F128::ONE);
/// assert!(F128::NAN.clamp(F128::NEG_ONE, F128::ONE).is_nan());
/// ```
#[inline]
#[track_caller]
#[must_use]
pub const fn clamp(mut self, min: F128, max: F128) -> F128 {
match partial_cmp(&min, &max) {
Some(Ordering::Less | Ordering::Equal) => {}
_ => panic!("need min <= max"),
}
if matches!(partial_cmp(&self, &min), Some(Ordering::Less)) {
self = min;
}
if matches!(partial_cmp(&self, &max), Some(Ordering::Greater)) {
self = max;
}
self
}
/// Returns the ordering between `self` and `other`.
///
/// Unlike the [`PartialOrd`] implementation, this method always returns an
/// order in the following sequence:
///
/// * NaN with the sign bit set
/// * &minus;∞
/// * negative normal numbers
/// * negative subnormal numbers
/// * &minus;0
/// * +0
/// * positive subnormal numbers
/// * positive normal numbers
/// * +∞
/// * NaN with the sign bit cleared
///
/// # Example
///
/// ```rust
/// use core::cmp::Ordering;
/// use fixed::F128;
///
/// let neg_nan = F128::NAN.copysign(F128::NEG_ONE);
/// let pos_nan = F128::NAN.copysign(F128::ONE);
/// let neg_inf = F128::NEG_INFINITY;
/// let pos_inf = F128::INFINITY;
/// let neg_zero = F128::NEG_ZERO;
/// let pos_zero = F128::ZERO;
///
/// assert_eq!(neg_nan.total_cmp(&neg_inf), Ordering::Less);
/// assert_eq!(pos_nan.total_cmp(&pos_inf), Ordering::Greater);
/// assert_eq!(neg_zero.total_cmp(&pos_zero), Ordering::Less);
/// ```
#[inline]
#[must_use]
pub const fn total_cmp(&self, other: &F128) -> Ordering {
let a = self.to_bits();
let b = other.to_bits();
match (self.is_sign_negative(), other.is_sign_negative()) {
(false, false) => cmp_bits(a, b),
(true, true) => cmp_bits(b, a),
(false, true) => Ordering::Greater,
(true, false) => Ordering::Less,
}
}
}
const fn cmp_bits(a: u128, b: u128) -> Ordering {
if a < b {
Ordering::Less
} else if a == b {
Ordering::Equal
} else {
Ordering::Greater
}
}
impl PartialEq for F128 {
#[inline]
fn eq(&self, other: &F128) -> bool {
if self.is_nan() || other.is_nan() {
return false;
}
let a = self.to_bits();
let b = other.to_bits();
// handle zero
if ((a | b) & !SIGN_MASK) == 0 {
return true;
}
a == b
}
}
impl PartialOrd for F128 {
#[inline]
fn partial_cmp(&self, other: &F128) -> Option<Ordering> {
partial_cmp(self, other)
}
}
#[inline]
const fn partial_cmp(a: &F128, b: &F128) -> Option<Ordering> {
if a.is_nan() || b.is_nan() {
return None;
}
let a_bits = a.to_bits();
let b_bits = b.to_bits();
// handle zero
if ((a_bits | b_bits) & !SIGN_MASK) == 0 {
return Some(Ordering::Equal);
}
match (a.is_sign_negative(), b.is_sign_negative()) {
(false, false) => Some(cmp_bits(a_bits, b_bits)),
(true, true) => Some(cmp_bits(b_bits, a_bits)),
(false, true) => Some(Ordering::Greater),
(true, false) => Some(Ordering::Less),
}
}
impl Hash for F128 {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
let mut bits = self.to_bits();
if bits == F128::NEG_ZERO.to_bits() {
bits = 0;
}
bits.hash(state);
}
}
impl Neg for F128 {
type Output = F128;
#[inline]
fn neg(self) -> F128 {
F128::from_bits(self.to_bits() ^ SIGN_MASK)
}
}
macro_rules! from_float {
($f:ident, $u:ident) => {
impl From<$f> for F128 {
fn from(src: $f) -> F128 {
const PREC_S: u32 = $f::MANTISSA_DIGITS;
const EXP_BITS_S: u32 = $u::BITS - PREC_S;
const EXP_BIAS_S: u32 = (1 << (EXP_BITS_S - 1)) - 1;
const SIGN_MASK_S: $u = 1 << ($u::BITS - 1);
const EXP_MASK_S: $u = ((1 << EXP_BITS_S) - 1) << (PREC_S - 1);
const MANT_MASK_S: $u = (1 << (PREC_S - 1)) - 1;
let b = src.to_bits();
let sign_bit_s = b & SIGN_MASK_S;
let exp_bits_s = b & EXP_MASK_S;
let mant_bits_s = b & MANT_MASK_S;
let sign_bit = u128::from(sign_bit_s) << (u128::BITS - $u::BITS);
if exp_bits_s == EXP_MASK_S {
if mant_bits_s == 0 {
// infinity
return F128::from_bits(sign_bit | EXP_MASK);
}
// NaN; set most significant mantissa bit
let mant_bits =
(u128::from(mant_bits_s) << (PREC - PREC_S)) | (1 << (PREC - 2));
return F128::from_bits(sign_bit | EXP_MASK | mant_bits);
}
if exp_bits_s == 0 {
// subnormal
// Example: if for f64 mantissa == 0b1011 == 11, then it has 60
// leading zeros, and 64 - 60 == 4 significant bits. The value is
//
// 0b1011 × 2^(-1021 - 53) == 0b1.011 × 2^(-1021 - 53 + 4 - 1)
//
// In F128, this is normal, with
// * mantissa == (1011 << ((113 - 1) - (4 - 1))) & MANT_MASK_128
// == (1011 << (113 - 4)) & MANT_MASK_128
// == (1011 << (113 - 64 + 60)) & MANT_MASK_128
// * unbiased exp == -1021 - 53 + 4 - 1
// == -1021 - 53 - 1 + 64 - 60
if mant_bits_s == 0 {
return F128::from_bits(sign_bit);
}
let lz = mant_bits_s.leading_zeros();
let mant_bits = (u128::from(mant_bits_s) << (PREC - $u::BITS + lz)) & MANT_MASK;
let unbiased_exp =
$f::MIN_EXP - PREC_S as i32 - 1 + $u::BITS as i32 - lz as i32;
let exp_bits = ((unbiased_exp + EXP_BIAS as i32) as u128) << (PREC - 1);
return F128::from_bits(sign_bit | exp_bits | mant_bits);
}
let mant_bits = u128::from(mant_bits_s) << (PREC - PREC_S);
let dbias = (EXP_BIAS - EXP_BIAS_S) as u128;
let exp_bits = (u128::from(exp_bits_s >> (PREC_S - 1)) + dbias) << (PREC - 1);
F128::from_bits(sign_bit | exp_bits | mant_bits)
}
}
};
}
#[cfg(feature = "nightly-float")]
impl From<f128> for F128 {
fn from(src: f128) -> F128 {
F128::from_bits(src.to_bits())
}
}
#[cfg(feature = "nightly-float")]
impl From<F128> for f128 {
fn from(src: F128) -> f128 {
f128::from_bits(src.to_bits())
}
}
from_float! { f64, u64 }
from_float! { f32, u32 }
from_float! { half_f16, u16 }
from_float! { half_bf16, u16 }
#[cfg(feature = "nightly-float")]
from_float! { f16, u16 }
/*
```rust
use core::cmp::Ord;
use rug::float::{Constant, Round};
use rug::{Assign, Float, Integer};
fn decimal_string(val: &Float, prec: i32) -> String {
let log10 = val.clone().log10();
let floor_log10 = log10.to_i32_saturating_round(Round::Down).unwrap();
let shift = u32::try_from(prec - 1 - floor_log10).unwrap();
let val = val.clone() * Integer::from(Integer::u_pow_u(10, shift));
let int = val.to_integer_round(Round::Down).unwrap().0;
let padding = "0".repeat(usize::try_from(-floor_log10.min(0)).unwrap());
let mut s = format!("{padding}{int}");
s.insert(1, '.');
s
}
fn hex_bits(bits: u128) -> String {
let mut s = format!("0x{bits:016X}");
for i in 0..7 {
s.insert(6 + 5 * i, '_');
}
s
}
fn print(doc: &str, name: &str, val: Float) {
println!();
println!(" /// {} = {}…", doc, decimal_string(&val, 6));
println!(" // {} = {}...", name, decimal_string(&val, 40));
let round = Float::with_val(113, &val);
let sign_bit = if round.is_sign_negative() {
1u128 << 127
} else {
0
};
let unbiased_exp = round.get_exp().unwrap();
assert!(-16_381 <= unbiased_exp && unbiased_exp <= 16_384);
let exp_bits = u128::from((unbiased_exp + 16_382).unsigned_abs()) << 112;
let unshifted_mant = round.get_significand().unwrap();
let mant = unshifted_mant.clone() >> (unshifted_mant.significant_bits() - 113);
let mant_128 = mant.to_u128_wrapping();
assert_eq!(mant_128 >> 112, 1);
let mant_bits = mant_128 & ((1 << 112) - 1);
println!(
" pub const {name}: F128 = F128::from_bits({});",
hex_bits(sign_bit | exp_bits | mant_bits)
);
}
fn float<T>(t: T) -> Float
where
Float: Assign<T>,
{
Float::with_val(1000, t)
}
fn main() {
println!("/// Basic mathematical constants.");
println!("pub mod consts {{");
println!(" use crate::F128;");
print("Archimedes’ constant, π", "PI", float(Constant::Pi));
print("A turn, τ", "TAU", float(Constant::Pi) * 2);
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("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("Euler’s number, e", "E", float(1).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());
println!("}}");
}
```
*/
/// Basic mathematical constants.
pub mod consts {
use crate::F128;
/// Archimedes’ constant, π = 3.14159…
// PI = 3.141592653589793238462643383279502884197...
pub const PI: F128 = F128::from_bits(0x4000_921F_B544_42D1_8469_898C_C517_01B8);
/// A turn, τ = 6.28318…
// TAU = 6.283185307179586476925286766559005768394...
pub const TAU: F128 = F128::from_bits(0x4001_921F_B544_42D1_8469_898C_C517_01B8);
/// π/2 = 1.57079…
// FRAC_PI_2 = 1.570796326794896619231321691639751442098...
pub const FRAC_PI_2: F128 = F128::from_bits(0x3FFF_921F_B544_42D1_8469_898C_C517_01B8);
/// π/3 = 1.04719…
// FRAC_PI_3 = 1.047197551196597746154214461093167628065...
pub const FRAC_PI_3: F128 = F128::from_bits(0x3FFF_0C15_2382_D736_5846_5BB3_2E0F_567B);
/// π/4 = 0.785398…
// FRAC_PI_4 = 0.7853981633974483096156608458198757210492...
pub const FRAC_PI_4: F128 = F128::from_bits(0x3FFE_921F_B544_42D1_8469_898C_C517_01B8);
/// π/6 = 0.523598…
// FRAC_PI_6 = 0.5235987755982988730771072305465838140328...
pub const FRAC_PI_6: F128 = F128::from_bits(0x3FFE_0C15_2382_D736_5846_5BB3_2E0F_567B);
/// π/8 = 0.392699…
// FRAC_PI_8 = 0.3926990816987241548078304229099378605246...
pub const FRAC_PI_8: F128 = F128::from_bits(0x3FFD_921F_B544_42D1_8469_898C_C517_01B8);
/// 1/π = 0.318309…
// FRAC_1_PI = 0.3183098861837906715377675267450287240689...
pub const FRAC_1_PI: F128 = F128::from_bits(0x3FFD_45F3_06DC_9C88_2A53_F84E_AFA3_EA6A);
/// 2/π = 0.636619…
// FRAC_2_PI = 0.6366197723675813430755350534900574481378...
pub const FRAC_2_PI: F128 = F128::from_bits(0x3FFE_45F3_06DC_9C88_2A53_F84E_AFA3_EA6A);
/// 2/√π = 1.12837…
// FRAC_2_SQRT_PI = 1.128379167095512573896158903121545171688...
pub const FRAC_2_SQRT_PI: F128 = F128::from_bits(0x3FFF_20DD_7504_29B6_D11A_E3A9_14FE_D7FE);
/// √2 = 1.41421…
// SQRT_2 = 1.414213562373095048801688724209698078569...
pub const SQRT_2: F128 = F128::from_bits(0x3FFF_6A09_E667_F3BC_C908_B2FB_1366_EA95);
/// 1/√2 = 0.707106…
// FRAC_1_SQRT_2 = 0.7071067811865475244008443621048490392848...
pub const FRAC_1_SQRT_2: F128 = F128::from_bits(0x3FFE_6A09_E667_F3BC_C908_B2FB_1366_EA95);
/// Euler’s number, e = 2.71828…
// E = 2.718281828459045235360287471352662497757...
pub const E: F128 = F128::from_bits(0x4000_5BF0_A8B1_4576_9535_5FB8_AC40_4E7A);
/// log<sub>2</sub> 10 = 3.32192…
// LOG2_10 = 3.321928094887362347870319429489390175864...
pub const LOG2_10: F128 = F128::from_bits(0x4000_A934_F097_9A37_15FC_9257_EDFE_9B60);
/// log<sub>2</sub> e = 1.44269…
// LOG2_E = 1.442695040888963407359924681001892137426...
pub const LOG2_E: F128 = F128::from_bits(0x3FFF_7154_7652_B82F_E177_7D0F_FDA0_D23A);
/// log<sub>10</sub> 2 = 0.301029…
// LOG10_2 = 0.3010299956639811952137388947244930267681...
pub const LOG10_2: F128 = F128::from_bits(0x3FFD_3441_3509_F79F_EF31_1F12_B358_16F9);
/// log<sub>10</sub> e = 0.434294…
// LOG10_E = 0.4342944819032518276511289189166050822943...
pub const LOG10_E: F128 = F128::from_bits(0x3FFD_BCB7_B152_6E50_E32A_6AB7_555F_5A68);
/// ln 2 = 0.693147…
// LN_2 = 0.6931471805599453094172321214581765680755...
pub const LN_2: F128 = F128::from_bits(0x3FFE_62E4_2FEF_A39E_F357_93C7_6730_07E6);
/// ln 10 = 2.30258…
// LN_10 = 2.302585092994045684017991454684364207601...
pub const LN_10: F128 = F128::from_bits(0x4000_26BB_1BBB_5551_582D_D4AD_AC57_05A6);
}
#[cfg(test)]
mod tests {
use crate::F128;
use crate::traits::FromFixed;
use half::{bf16 as half_bf16, f16 as half_f16};
// Apart from F128 include f16, bf16, f32, f64 as a sanity check for the tests.
struct Params {
mantissa_digits: u32,
min_exp: i32,
max_exp: i32,
digits: u32,
min_10_exp: i32,
max_10_exp: i32,
}
impl Params {
#[track_caller]
fn check(self) {
let p = f64::from(self.mantissa_digits);
let e_min = f64::from(self.min_exp);
let e_max = f64::from(self.max_exp);
assert_eq!(self.digits, ((p - 1.) * 2f64.log10()).floor() as u32);
assert_eq!(self.min_10_exp, ((e_min - 1.) * 2f64.log10()).ceil() as i32);
assert_eq!(
self.max_10_exp,
((-(-p).exp2()).ln_1p() / 10f64.ln() + e_max * 2f64.log10()).floor() as i32
);
}
}
#[test]
fn decimal_constants_f16() {
let params = Params {
mantissa_digits: half_f16::MANTISSA_DIGITS,
min_exp: half_f16::MIN_EXP,
max_exp: half_f16::MAX_EXP,
digits: half_f16::DIGITS,
min_10_exp: half_f16::MIN_10_EXP,
max_10_exp: half_f16::MAX_10_EXP,
};
params.check();
}
#[test]
fn decimal_constants_bf16() {
let params = Params {
mantissa_digits: half_bf16::MANTISSA_DIGITS,
min_exp: half_bf16::MIN_EXP,
max_exp: half_bf16::MAX_EXP,
digits: half_bf16::DIGITS,
min_10_exp: half_bf16::MIN_10_EXP,
max_10_exp: half_bf16::MAX_10_EXP,
};
params.check();
}
#[test]
fn decimal_constants_f32() {
let params = Params {
mantissa_digits: f32::MANTISSA_DIGITS,
min_exp: f32::MIN_EXP,
max_exp: f32::MAX_EXP,
digits: f32::DIGITS,
min_10_exp: f32::MIN_10_EXP,
max_10_exp: f32::MAX_10_EXP,
};
params.check();
}
#[test]
fn decimal_constants_f64() {
let params = Params {
mantissa_digits: f64::MANTISSA_DIGITS,
min_exp: f64::MIN_EXP,
max_exp: f64::MAX_EXP,
digits: f64::DIGITS,
min_10_exp: f64::MIN_10_EXP,
max_10_exp: f64::MAX_10_EXP,
};
params.check();
}
#[test]
fn decimal_constants_f128() {
let params = Params {
mantissa_digits: F128::MANTISSA_DIGITS,
min_exp: F128::MIN_EXP,
max_exp: F128::MAX_EXP,
digits: F128::DIGITS,
min_10_exp: F128::MIN_10_EXP,
max_10_exp: F128::MAX_10_EXP,
};
params.check();
}
#[test]
fn math_constants() {
use crate::consts as fix;
use crate::f128::consts as f128;
assert_eq!(f128::PI, F128::from_fixed(fix::PI));
assert_eq!(f128::TAU, F128::from_fixed(fix::TAU));
assert_eq!(f128::FRAC_PI_2, F128::from_fixed(fix::FRAC_PI_2));
assert_eq!(f128::FRAC_PI_3, F128::from_fixed(fix::FRAC_PI_3));
assert_eq!(f128::FRAC_PI_4, F128::from_fixed(fix::FRAC_PI_4));
assert_eq!(f128::FRAC_PI_6, F128::from_fixed(fix::FRAC_PI_6));
assert_eq!(f128::FRAC_PI_8, F128::from_fixed(fix::FRAC_PI_8));
assert_eq!(f128::FRAC_1_PI, F128::from_fixed(fix::FRAC_1_PI));
assert_eq!(f128::FRAC_2_PI, F128::from_fixed(fix::FRAC_2_PI));
assert_eq!(f128::FRAC_2_SQRT_PI, F128::from_fixed(fix::FRAC_2_SQRT_PI));
assert_eq!(f128::SQRT_2, F128::from_fixed(fix::SQRT_2));
assert_eq!(f128::FRAC_1_SQRT_2, F128::from_fixed(fix::FRAC_1_SQRT_2));
assert_eq!(f128::E, F128::from_fixed(fix::E));
assert_eq!(f128::LOG2_10, F128::from_fixed(fix::LOG2_10));
assert_eq!(f128::LOG2_E, F128::from_fixed(fix::LOG2_E));
assert_eq!(f128::LOG10_2, F128::from_fixed(fix::LOG10_2));
assert_eq!(f128::LOG10_E, F128::from_fixed(fix::LOG10_E));
assert_eq!(f128::LN_2, F128::from_fixed(fix::LN_2));
assert_eq!(f128::LN_10, F128::from_fixed(fix::LN_10));
}
#[test]
fn from_f64() {
// normal
assert_eq!(F128::from(1f64), F128::ONE);
assert_eq!(F128::from(-1f64), F128::NEG_ONE);
// infinity
assert_eq!(F128::from(f64::INFINITY), F128::INFINITY);
assert_eq!(F128::from(f64::NEG_INFINITY), F128::NEG_INFINITY);
// NaN
assert!(F128::from(f64::NAN).is_nan());
// zero
assert_eq!(F128::from(0f64), F128::ZERO);
assert_eq!(F128::from(-0f64), F128::ZERO);
assert!(F128::from(0f64).is_sign_positive());
assert!(F128::from(-0f64).is_sign_negative());
// subnormal
let exp_shift = F128::MANTISSA_DIGITS - 1;
// minimum f64 positive subnormal = 2^(-1021 - 53)
// mantissa = 0
// biased exponent = 16383 - 1021 - 53
let exp = (F128::MAX_EXP - 1 + f64::MIN_EXP - f64::MANTISSA_DIGITS as i32) as u128;
assert_eq!(
F128::from(f64::from_bits(1)),
F128::from_bits(exp << exp_shift)
);
// minimum f64 positive subnormal * 0b1011 = 0b1.011 * 2^(-1021 - 53 + 3)
// mantissa = .011 << (113 - 1) = 011 << (113 - 1 - 3)
// biased exponent = 16383 - 1021 - 53 + 3
let mantissa = 3u128 << (F128::MANTISSA_DIGITS - 1 - 3);
let exp = exp + 3;
assert_eq!(
F128::from(f64::from_bits((1 << 63) | 11)),
F128::from_bits((1 << 127) | (exp << exp_shift) | mantissa)
);
}
#[test]
fn from_f32() {
// normal
assert_eq!(F128::from(1f32), F128::ONE);
assert_eq!(F128::from(-1f32), F128::NEG_ONE);
// infinity
assert_eq!(F128::from(f32::INFINITY), F128::INFINITY);
assert_eq!(F128::from(f32::NEG_INFINITY), F128::NEG_INFINITY);
// NaN
assert!(F128::from(f32::NAN).is_nan());
// zero
assert_eq!(F128::from(0f32), F128::ZERO);
assert_eq!(F128::from(-0f32), F128::ZERO);
assert!(F128::from(0f32).is_sign_positive());
assert!(F128::from(-0f32).is_sign_negative());
// subnormal
let exp_shift = F128::MANTISSA_DIGITS - 1;
// minimum f32 positive subnormal = 2^(-125 - 24)
// mantissa = 0
// biased exponent = 16383 - 125 - 24
let exp = (F128::MAX_EXP - 1 + f32::MIN_EXP - f32::MANTISSA_DIGITS as i32) as u128;
assert_eq!(
F128::from(f32::from_bits(1)),
F128::from_bits(exp << exp_shift)
);
// minimum f32 positive subnormal * 0b1011 = 0b1.011 * 2^(-125 - 24 + 3)
// mantissa = .011 << (113 - 1) = 011 << (113 - 1 - 3)
// biased exponent = 16383 - 125 - 24 + 3
let mantissa = 3u128 << (F128::MANTISSA_DIGITS - 1 - 3);
let exp = exp + 3;
assert_eq!(
F128::from(f32::from_bits((1 << 31) | 11)),
F128::from_bits((1 << 127) | (exp << exp_shift) | mantissa)
);
}
#[test]
fn from_f16() {
// normal
assert_eq!(F128::from(half_f16::ONE), F128::ONE);
assert_eq!(F128::from(half_f16::NEG_ONE), F128::NEG_ONE);
// infinity
assert_eq!(F128::from(half_f16::INFINITY), F128::INFINITY);
assert_eq!(F128::from(half_f16::NEG_INFINITY), F128::NEG_INFINITY);
// NaN
assert!(F128::from(half_f16::NAN).is_nan());
// zero
assert_eq!(F128::from(half_f16::ZERO), F128::ZERO);
assert_eq!(F128::from(half_f16::NEG_ZERO), F128::ZERO);
assert!(F128::from(half_f16::ZERO).is_sign_positive());
assert!(F128::from(half_f16::NEG_ZERO).is_sign_negative());
// subnormal
let exp_shift = F128::MANTISSA_DIGITS - 1;
// minimum f16 positive subnormal = 2^(-13 - 11)
// mantissa = 0
// biased exponent = 16383 - 13 - 11
let exp =
(F128::MAX_EXP - 1 + half_f16::MIN_EXP - half_f16::MANTISSA_DIGITS as i32) as u128;
assert_eq!(
F128::from(half_f16::from_bits(1)),
F128::from_bits(exp << exp_shift)
);
// minimum f16 positive subnormal * 0b1011 = 0b1.011 * 2^(-13 - 11 + 3)
// mantissa = .011 << (113 - 1) = 011 << (113 - 1 - 3)
// biased exponent = 16383 - 13 - 11 + 3
let mantissa = 3u128 << (F128::MANTISSA_DIGITS - 1 - 3);
let exp = exp + 3;
assert_eq!(
F128::from(half_f16::from_bits((1 << 15) | 11)),
F128::from_bits((1 << 127) | (exp << exp_shift) | mantissa)
);
}
#[test]
fn from_bf16() {
// normal
assert_eq!(F128::from(half_bf16::ONE), F128::ONE);
assert_eq!(F128::from(half_bf16::NEG_ONE), F128::NEG_ONE);
// infinity
assert_eq!(F128::from(half_bf16::INFINITY), F128::INFINITY);
assert_eq!(F128::from(half_bf16::NEG_INFINITY), F128::NEG_INFINITY);
// NaN
assert!(F128::from(half_bf16::NAN).is_nan());
// zero
assert_eq!(F128::from(half_bf16::ZERO), F128::ZERO);
assert_eq!(F128::from(half_bf16::NEG_ZERO), F128::ZERO);
assert!(F128::from(half_bf16::ZERO).is_sign_positive());
assert!(F128::from(half_bf16::NEG_ZERO).is_sign_negative());
// subnormal
let exp_shift = F128::MANTISSA_DIGITS - 1;
// minimum half_bf16 positive subnormal = 2^(-125 - 8)
// mantissa = 0
// biased exponent = 16383 - 125 - 8
let exp =
(F128::MAX_EXP - 1 + half_bf16::MIN_EXP - half_bf16::MANTISSA_DIGITS as i32) as u128;
assert_eq!(
F128::from(half_bf16::from_bits(1)),
F128::from_bits(exp << exp_shift)
);
// minimum bf16 positive subnormal * 0b1011 = 0b1.011 * 2^(-125 - 8 + 3)
// mantissa = .011 << (113 - 1) = 011 << (113 - 1 - 3)
// biased exponent = 16383 - 125 - 8 + 3
let mantissa = 3u128 << (F128::MANTISSA_DIGITS - 1 - 3);
let exp = exp + 3;
assert_eq!(
F128::from(half_bf16::from_bits((1 << 15) | 11)),
F128::from_bits((1 << 127) | (exp << exp_shift) | mantissa)
);
}
}