Skip to content

Commit db91fb0

Browse files
committed
fmt of floating points in plain code, defragemented
1 parent 47c00e2 commit db91fb0

23 files changed

+1610
-3290
lines changed

library/core/src/fmt/float.rs

Lines changed: 335 additions & 187 deletions
Large diffs are not rendered by default.

library/core/src/fmt/mod.rs

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell};
66
use crate::char::EscapeDebugExtArgs;
77
use crate::hint::assert_unchecked;
88
use crate::marker::{PhantomData, PointeeSized};
9-
use crate::num::fmt as numfmt;
109
use crate::ops::Deref;
1110
use crate::ptr::NonNull;
1211
use crate::{iter, mem, result, str};
@@ -1779,7 +1778,7 @@ impl<'a> Formatter<'a> {
17791778

17801779
/// Write a formatted integer to the Formatter. The `digits` string should
17811780
/// hold the representation of the absolute value, without sign. Whether a
1782-
/// sign ("+" or "-") is written depends both on format paramaters and on
1781+
/// sign ("+" or "-") is written depends both on format parameters and on
17831782
/// `is_nonnegative`. The `prefix` (e.g., "0x") is written when alternate.
17841783
/// Padding may be included when width is Some. The precision is ignored
17851784
/// for integers.
@@ -2005,53 +2004,6 @@ impl<'a> Formatter<'a> {
20052004
Ok(PostPadding::new(fill, padding - padding_left))
20062005
}
20072006

2008-
/// Takes the formatted parts and applies the padding.
2009-
///
2010-
/// Assumes that the caller already has rendered the parts with required precision,
2011-
/// so that `self.precision` can be ignored.
2012-
///
2013-
/// # Safety
2014-
///
2015-
/// Any `numfmt::Part::Copy` parts in `formatted` must contain valid UTF-8.
2016-
unsafe fn pad_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result {
2017-
unsafe fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result {
2018-
// SAFETY: This is used for `numfmt::Part::Num` and `numfmt::Part::Copy`.
2019-
// It's safe to use for `numfmt::Part::Num` since every char `c` is between
2020-
// `b'0'` and `b'9'`, which means `s` is valid UTF-8. It's safe to use for
2021-
// `numfmt::Part::Copy` due to this function's precondition.
2022-
buf.write_str(unsafe { str::from_utf8_unchecked(s) })
2023-
}
2024-
2025-
let sign = formatted.sign;
2026-
let out_len = formatted.len() - sign.len();
2027-
self.pad_number(sign, out_len, |f: &mut Self| {
2028-
for part in formatted.parts {
2029-
match *part {
2030-
numfmt::Part::Zero(nzeroes) => {
2031-
f.write_zeroes(nzeroes)?;
2032-
}
2033-
numfmt::Part::Num(mut v) => {
2034-
let mut s = [0; 5];
2035-
let len = part.len();
2036-
for c in s[..len].iter_mut().rev() {
2037-
*c = b'0' + (v % 10) as u8;
2038-
v /= 10;
2039-
}
2040-
// SAFETY: Per the precondition.
2041-
unsafe {
2042-
write_bytes(f.buf, &s[..len])?;
2043-
}
2044-
}
2045-
// SAFETY: Per the precondition.
2046-
numfmt::Part::Copy(buf) => unsafe {
2047-
write_bytes(f.buf, buf)?;
2048-
},
2049-
}
2050-
}
2051-
Ok(())
2052-
})
2053-
}
2054-
20552007
/// Write n ASCII digits (U+0030) to the Formatter.
20562008
pub(crate) fn write_zeroes(&mut self, n: usize) -> Result {
20572009
#[cfg(feature = "optimize_for_size")]
Lines changed: 85 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,107 @@
11
//! Decodes a floating-point value into individual parts and error ranges.
22
3-
use crate::num::FpCategory;
4-
use crate::num::dec2flt::float::RawFloat;
3+
use crate::mem::size_of;
54

6-
/// Decoded unsigned finite value, such that:
5+
/// Generic representation of finite floating-point values up to 64-bit wide.
6+
/// The absolute value equals `mant * 2^exp`. All real values `x` such that:
77
///
8-
/// - The original value equals to `mant * 2^exp`.
8+
/// lower < x < upper
9+
///
10+
/// (or `lower < x ≤ upper` in the tie-to-even case) round to this value under
11+
/// IEEE 754 round-to-nearest rules, where:
12+
///
13+
/// lower = (mant − minus) * 2^exp
14+
/// upper = (mant + plus) * 2^exp
915
///
10-
/// - Any number from `(mant - minus) * 2^exp` to `(mant + plus) * 2^exp` will
11-
/// round to the original value. The range is inclusive only when
12-
/// `inclusive` is `true`.
1316
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14-
pub struct Decoded {
15-
/// The scaled mantissa.
17+
pub struct Decoded64 {
18+
/// Scaled mantissa. The scaling is chosen such that the rounding boundaries
19+
/// are integral when expressed as `mant ± {minus, plus}`.
1620
pub mant: u64,
17-
/// The lower error range.
21+
22+
/// Distance from `mant` to the lower rounding-boundary.
1823
pub minus: u64,
19-
/// The upper error range.
24+
/// Distance from `mant` to the upper rounding-boundary.
2025
pub plus: u64,
21-
/// The shared exponent in base 2.
22-
pub exp: i16,
23-
/// True when the error range is inclusive.
24-
///
25-
/// In IEEE 754, this is true when the original mantissa was even.
26-
pub inclusive: bool,
27-
}
2826

29-
/// Decoded unsigned value.
30-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
31-
pub enum FullDecoded {
32-
/// Not-a-number.
33-
Nan,
34-
/// Infinities, either positive or negative.
35-
Infinite,
36-
/// Zero, either positive or negative.
37-
Zero,
38-
/// Finite numbers with further decoded fields.
39-
Finite(Decoded),
40-
}
27+
/// Base-2 exponent for `mant`, `minus`, and `plus`.
28+
pub exp: isize,
4129

42-
/// A floating point type which can be `decode`d.
43-
pub trait DecodableFloat: RawFloat + Copy {
44-
/// The minimum positive normalized value.
45-
fn min_pos_norm_value() -> Self;
30+
/// Indicates whether an exact tie at the upper rounding-boundary (i.e., the
31+
/// midpoint between this value and its next representable neighbor) rounds
32+
/// to this value. It applies only to the upper boundary; the lower boundary
33+
/// is always exclusive. This follows IEEE 754 round-ties-to-even semantics
34+
/// and is true iff the original significand was even.
35+
pub tie_to_even: bool,
4636
}
4737

48-
#[cfg(target_has_reliable_f16)]
49-
impl DecodableFloat for f16 {
50-
fn min_pos_norm_value() -> Self {
51-
f16::MIN_POSITIVE
52-
}
53-
}
38+
macro_rules! floats {
39+
($($T:ident)*) => {
40+
$(
5441

55-
impl DecodableFloat for f32 {
56-
fn min_pos_norm_value() -> Self {
57-
f32::MIN_POSITIVE
58-
}
59-
}
42+
/// Decode a floating-point into its integer components. The tuple in
43+
/// return contains the mantissa m and exponent e, such that original
44+
/// value equals `m * 2^e`, ignoring the sign.
45+
///
46+
/// For normal numbers: mantissa includes the implied leading 1.
47+
/// For denormal numbers: mantissa is shifted to maintain the equation.
48+
const fn ${concat(mant_and_exp_, $T)}(v: $T) -> (u64, isize) {
49+
const ENC_BITS: usize = size_of::<$T>() * 8;
50+
// The encoding of the sign resides in the most significant bit.
51+
const SIGN_ENC_BITS: usize = 1;
52+
// The encoding of the mantissa resides in the least-significant
53+
// bits.
54+
const MANT_ENC_BITS: usize = $T::MANTISSA_DIGITS as usize - 1;
55+
// The encoding of the exponent resides in the remaining bits,
56+
// inbetween sign and the mantissa.
57+
const EXP_ENC_BITS: usize = ENC_BITS - (SIGN_ENC_BITS + MANT_ENC_BITS);
6058

61-
impl DecodableFloat for f64 {
62-
fn min_pos_norm_value() -> Self {
63-
f64::MIN_POSITIVE
64-
}
65-
}
59+
let enc = v.to_bits();
60+
let exp_enc = (enc << SIGN_ENC_BITS) >> (SIGN_ENC_BITS + MANT_ENC_BITS);
61+
let mant_enc = enc & ((1 << MANT_ENC_BITS) - 1);
62+
63+
const EXP_BIAS: isize = (1 << (EXP_ENC_BITS - 1)) - 1;
64+
let exp = exp_enc as isize - (EXP_BIAS + MANT_ENC_BITS as isize);
6665

67-
/// Returns a sign (true when negative) and `FullDecoded` value
68-
/// from given floating point number.
69-
pub fn decode<T: DecodableFloat>(v: T) -> (/*negative?*/ bool, FullDecoded) {
70-
let (mant, exp, sign) = v.integer_decode();
71-
let even = (mant & 1) == 0;
72-
let decoded = match v.classify() {
73-
FpCategory::Nan => FullDecoded::Nan,
74-
FpCategory::Infinite => FullDecoded::Infinite,
75-
FpCategory::Zero => FullDecoded::Zero,
76-
FpCategory::Subnormal => {
77-
// neighbors: (mant - 2, exp) -- (mant, exp) -- (mant + 2, exp)
78-
// Float::integer_decode always preserves the exponent,
79-
// so the mantissa is scaled for subnormals.
80-
FullDecoded::Finite(Decoded { mant, minus: 1, plus: 1, exp, inclusive: even })
66+
let mant = if exp_enc != 0 {
67+
// Normal numbers have an implied leading 1 to the mantissa
68+
// bits.
69+
mant_enc | 1 << MANT_ENC_BITS
70+
} else {
71+
// Account for the effective +1 on exponents of denormal numbers.
72+
mant_enc << 1
73+
};
74+
75+
const _: () = assert!(ENC_BITS <= 64);
76+
(mant as u64, exp)
8177
}
82-
FpCategory::Normal => {
83-
let minnorm = <T as DecodableFloat>::min_pos_norm_value().integer_decode();
84-
if mant == minnorm.0 {
85-
// neighbors: (maxmant, exp - 1) -- (minnormmant, exp) -- (minnormmant + 1, exp)
86-
// where maxmant = minnormmant * 2 - 1
87-
FullDecoded::Finite(Decoded {
88-
mant: mant << 2,
89-
minus: 1,
90-
plus: 2,
91-
exp: exp - 2,
92-
inclusive: even,
93-
})
78+
79+
/// Parse a finite, non-zero floating-point into the generic structure.
80+
pub fn ${concat(decode_, $T)}(v: $T) -> Decoded64 {
81+
let (mant, exp) = ${concat(mant_and_exp_, $T)}(v);
82+
83+
if v.is_subnormal() {
84+
// Subnormal floats have doubled spacing between representable values.
85+
// The boundaries are symmetric around `mant` in this scaled integer space.
86+
return Decoded64 { mant: mant, minus: 1, plus: 1, exp: exp, tie_to_even: true };
87+
}
88+
debug_assert!(v.is_normal());
89+
90+
let is_even = (mant & 1) == 0;
91+
92+
const MIN_POS_MANT: u64 = ${concat(mant_and_exp_, $T)}($T::MIN_POSITIVE).0;
93+
const _: () = assert!(MIN_POS_MANT != 0);
94+
if mant == MIN_POS_MANT {
95+
// The previous float of the first normal number is the largest subnormal.
96+
// The upper boundary is asymmetrically farther away than the lower boundary.
97+
Decoded64 { mant: mant << 2, minus: 1, plus: 2, exp: exp - 2, tie_to_even: is_even }
9498
} else {
95-
// neighbors: (mant - 1, exp) -- (mant, exp) -- (mant + 1, exp)
96-
FullDecoded::Finite(Decoded {
97-
mant: mant << 1,
98-
minus: 1,
99-
plus: 1,
100-
exp: exp - 1,
101-
inclusive: even,
102-
})
99+
Decoded64 { mant: mant << 1, minus: 1, plus: 1, exp: exp - 1, tie_to_even: is_even }
103100
}
104101
}
102+
103+
)*
105104
};
106-
(sign < 0, decoded)
107105
}
106+
107+
floats! { f16 f32 f64 }
Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
//! The exponent estimator.
22
3-
/// Finds `k_0` such that `10^(k_0-1) < mant * 2^exp <= 10^(k_0+1)`.
3+
/// Estimates the base-10 scaling factor `k = ceil(log10(mant * 2^exp))`.
44
///
5-
/// This is used to approximate `k = ceil(log_10 (mant * 2^exp))`;
6-
/// the true `k` is either `k_0` or `k_0+1`.
5+
/// Returns a lower-bound estimate `r` such that:
6+
///
7+
/// k ∈ { r, r + 1 }
78
#[doc(hidden)]
8-
pub fn estimate_scaling_factor(mant: u64, exp: i16) -> i16 {
9-
// 2^(nbits-1) < mant <= 2^nbits if mant > 0
9+
pub fn estimate_scaling_factor(mant: u64, exp: isize) -> isize {
10+
// 2^(nbits - 1) < mant <= 2^nbits if mant > 0
1011
let nbits = 64 - (mant - 1).leading_zeros() as i64;
11-
// 1292913986 = floor(2^32 * log_10 2)
12-
// therefore this always underestimates (or is exact), but not much.
13-
(((nbits + exp as i64) * 1292913986) >> 32) as i16
12+
let n = nbits + exp as i64;
13+
// log₁₀(2ⁿ) = n × log₁₀(2)
14+
//
15+
// To multiply with log₁₀(2) as an integer, the fraction (≈0.3) is scaled.
16+
//
17+
// n × log₁₀(2) = (n × log₁₀(2) × C) ÷ C
18+
//
19+
// With C = 2³², and ⌊log₁₀(2) × 2³²⌋ = 1292913986, we can compute:
20+
((n * 1292913986) >> 32) as isize
1421
}

0 commit comments

Comments
 (0)