decstr/bitstring/
fixed64.rs

1use crate::{
2    binary::{
3        encode_max,
4        encode_min,
5        FixedBinaryBuf,
6    },
7    text::ArrayTextBuf,
8};
9
10/**
11A [64bit decimal number](https://en.wikipedia.org/wiki/Decimal64_floating-point_format).
12*/
13#[derive(Clone, Copy)]
14pub struct Bitstring64(FixedBinaryBuf<8, i32>);
15
16/**
17Basic mathematical constants.
18*/
19impl Bitstring64 {
20    /// 0
21    pub const ZERO: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([0, 0, 0, 0, 0, 0, 56, 34]));
22
23    /// 1
24    pub const ONE: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([1, 0, 0, 0, 0, 0, 56, 34]));
25
26    /// -1
27    pub const NEG_ONE: Self =
28        Bitstring64(FixedBinaryBuf::from_le_bytes([1, 0, 0, 0, 0, 0, 56, 162]));
29
30    /// Archimedes' constant (π)
31    pub const PI: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
32        187, 63, 59, 181, 174, 193, 252, 45,
33    ]));
34
35    /// The full circle constant (τ)
36    ///
37    /// Equal to 2π.
38    pub const TAU: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
39        234, 230, 115, 216, 50, 43, 253, 57,
40    ]));
41
42    /// π/2
43    pub const FRAC_PI_2: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
44        30, 107, 111, 154, 254, 240, 254, 37,
45    ]));
46
47    /// π/3
48    pub const FRAC_PI_3: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
49        251, 234, 19, 237, 62, 71, 252, 37,
50    ]));
51
52    /// π/4
53    pub const FRAC_PI_4: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
54        72, 238, 55, 142, 119, 203, 255, 33,
55    ]));
56
57    /// π/6
58    pub const FRAC_PI_6: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
59        94, 121, 91, 191, 183, 163, 254, 33,
60    ]));
61
62    /// π/8
63    pub const FRAC_PI_8: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
64        164, 123, 189, 192, 215, 186, 253, 33,
65    ]));
66
67    /// 1/π
68    pub const FRAC_1_PI: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
69        154, 175, 226, 112, 98, 152, 253, 33,
70    ]));
71
72    /// 2/π
73    pub const FRAC_2_PI: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
74        139, 158, 39, 127, 198, 54, 255, 33,
75    ]));
76
77    /// 2/sqrt(π)
78    pub const FRAC_2_SQRT_PI: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
79        146, 110, 113, 78, 126, 168, 252, 37,
80    ]));
81
82    /// sqrt(2)
83    pub const SQRT_2: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
84        91, 204, 39, 238, 68, 20, 254, 37,
85    ]));
86
87    /// 1/sqrt(2)
88    pub const FRAC_1_SQRT_2: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
89        199, 170, 179, 184, 33, 135, 255, 33,
90    ]));
91
92    /// Euler's number (e)
93    pub const E: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
94        69, 100, 233, 210, 66, 152, 255, 41,
95    ]));
96
97    /// log<sub>2</sub>(10)
98    pub const LOG2_10: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
99        226, 61, 172, 133, 107, 161, 253, 45,
100    ]));
101
102    /// log<sub>2</sub>(e)
103    pub const LOG2_E: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
104        237, 185, 1, 196, 214, 66, 254, 37,
105    ]));
106
107    /// log<sub>10</sub>(2)
108    pub const LOG10_2: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
109        143, 140, 253, 105, 10, 129, 253, 33,
110    ]));
111
112    /// log<sub>10</sub>(e)
113    pub const LOG10_E: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
114        81, 53, 182, 160, 86, 52, 254, 33,
115    ]));
116
117    /// ln(2)
118    pub const LN_2: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
119        205, 102, 171, 200, 49, 59, 255, 33,
120    ]));
121
122    /// ln(10)
123    pub const LN_10: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
124        69, 120, 170, 195, 178, 130, 253, 41,
125    ]));
126}
127
128impl Bitstring64 {
129    /// The radix or base of the internal representation.
130    pub const RADIX: u32 = 10;
131
132    /**
133    The number of digits in base 10 that can be represented without loss of precision.
134
135    This constant indicates the total count of significant decimal digits in the
136    significand, regardless of the decimal point's position. For instance
137    1234567 and 123.4567, both contain `DIGITS` digits.
138    */
139    pub const DIGITS: u32 = 16;
140
141    /**
142    [Machine epsilon] value.
143
144    This is the difference between `1.0` and the next larger representable number.
145
146    [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon
147    */
148    pub const EPSILON: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([1, 0, 0, 0, 0, 0, 0, 34]));
149
150    /// Smallest finite value.
151    pub const MIN: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
152        255, 252, 243, 207, 63, 255, 248, 247,
153    ]));
154
155    /// Smallest positive normal value.
156    pub const MIN_POSITIVE: Self =
157        Bitstring64(FixedBinaryBuf::from_le_bytes([1, 0, 0, 0, 0, 0, 0, 0]));
158
159    /// Largest finite value.
160    pub const MAX: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([
161        255, 252, 243, 207, 63, 255, 248, 119,
162    ]));
163
164    /// Minimum possible normal power of 10 exponent.
165    pub const MIN_10_EXP: i32 = -398;
166
167    /// Maximum possible normal power of 10 exponent.
168    pub const MAX_10_EXP: i32 = 369;
169
170    /// Not a Number (NaN), with a zero payload.
171    pub const NAN: Self = Bitstring64(FixedBinaryBuf::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 124]));
172
173    /// Infinity (∞).
174    pub const INFINITY: Self =
175        Bitstring64(FixedBinaryBuf::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 120]));
176
177    /// Negative infinity (−∞).
178    pub const NEG_INFINITY: Self =
179        Bitstring64(FixedBinaryBuf::from_le_bytes([0, 0, 0, 0, 0, 0, 0, 248]));
180}
181
182impl Bitstring64 {
183    /**
184    Create a decimal from its representation as a byte array in little endian.
185
186    This matches the internal byte representation of the decimal, regardless of the platform.
187    */
188    #[inline]
189    pub const fn from_le_bytes(bytes: [u8; 8]) -> Self {
190        Self(FixedBinaryBuf::from_le_bytes(bytes))
191    }
192
193    /**
194    Create a decimal from its representation as a byte array in big endian.
195    */
196    #[inline]
197    pub const fn from_be_bytes(bytes: [u8; 8]) -> Self {
198        Self(FixedBinaryBuf::from_le_bytes([
199            bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0],
200        ]))
201    }
202
203    /**
204    Return the memory representation of this decimal as a byte array in little-endian byte order.
205
206    This matches the internal byte representation of the decimal, regardless of the platform.
207    */
208    #[inline]
209    pub const fn as_le_bytes(&self) -> &[u8; 8] {
210        // Even on big-endian platforms we always encode numbers in little-endian order
211        self.0.as_le_bytes()
212    }
213
214    /**
215    Return the memory representation of this decimal as a byte array in big-endian
216    (network) byte order.
217    */
218    #[inline]
219    pub const fn to_be_bytes(&self) -> [u8; 8] {
220        let b = self.0.as_le_bytes();
221        [b[7], b[6], b[5], b[4], b[3], b[2], b[1], b[0]]
222    }
223
224    /**
225    Create a decimal with the finite value zero.
226    */
227    pub fn zero() -> Self {
228        Self::from(0u8)
229    }
230
231    /**
232    Create a decimal with its maximum finite value.
233    */
234    pub fn max() -> Self {
235        let mut buf = FixedBinaryBuf::ZERO;
236
237        encode_max(&mut buf, false);
238
239        Self(buf)
240    }
241
242    /**
243    Create a decimal with its minimum finite value.
244    */
245    pub fn min() -> Self {
246        let mut buf = FixedBinaryBuf::ZERO;
247
248        encode_max(&mut buf, true);
249
250        Self(buf)
251    }
252
253    /**
254    Create a decimal with its minimum positive non-zero value.
255    */
256    pub fn min_positive() -> Self {
257        let mut buf = FixedBinaryBuf::ZERO;
258
259        encode_min(&mut buf, false);
260
261        Self(buf)
262    }
263}
264
265classify!(Bitstring64);
266
267try_s2d!(ArrayTextBuf::<64> => Bitstring64);
268d2s!(Bitstring64);
269
270f2d!(f32 => from_f32 => Bitstring64);
271try_f2d!(f64 => from_f64 => Bitstring64);
272
273try_d2f!(Bitstring64 => to_f32 => f32);
274try_d2f!(Bitstring64 => to_f64 => f64);
275
276i2d!(i8 => from_i8 => Bitstring64);
277i2d!(i16 => from_i16 => Bitstring64);
278i2d!(i32 => from_i32 => Bitstring64);
279try_i2d!(i64 => from_i64 => Bitstring64);
280try_i2d!(i128 => from_i128 => Bitstring64);
281
282try_d2i!(Bitstring64 => to_i8 => i8);
283try_d2i!(Bitstring64 => to_i16 => i16);
284try_d2i!(Bitstring64 => to_i32 => i32);
285try_d2i!(Bitstring64 => to_i64 => i64);
286try_d2i!(Bitstring64 => to_i128 => i128);
287
288i2d!(u8 => from_u8 => Bitstring64);
289i2d!(u16 => from_u16 => Bitstring64);
290i2d!(u32 => from_u32 => Bitstring64);
291try_i2d!(u64 => from_u64 => Bitstring64);
292try_i2d!(u128 => from_u128 => Bitstring64);
293
294try_d2i!(Bitstring64 => to_u8 => u8);
295try_d2i!(Bitstring64 => to_u16 => u16);
296try_d2i!(Bitstring64 => to_u32 => u32);
297try_d2i!(Bitstring64 => to_u64 => u64);
298try_d2i!(Bitstring64 => to_u128 => u128);
299
300#[cfg(test)]
301mod tests {
302    use super::*;
303
304    #[test]
305    fn consts_64() {
306        use core::str::FromStr;
307
308        // helper fn
309        fn is_eq(a: Bitstring64, b: Bitstring64) {
310            assert_eq!(a.as_le_bytes(), b.as_le_bytes());
311        }
312        // helper fn
313        fn is_eq_f(a: Bitstring64, s: &str) {
314            assert_eq!(
315                a.to_string(),
316                s.chars()
317                    .take((Bitstring64::DIGITS + 1) as usize)
318                    .collect::<String>()
319            );
320        }
321
322        is_eq(Bitstring64::ZERO, Bitstring64::from_str("0").unwrap());
323        is_eq(Bitstring64::ONE, Bitstring64::from_str("1").unwrap());
324        is_eq(Bitstring64::NEG_ONE, Bitstring64::from_str("-1").unwrap());
325
326        // 37 char strings extracted from https://doc.rust-lang.org/stable/core/f64/consts/index.html
327        const PI: &str = "3.14159265358979323846264338327950288";
328        const TAU: &str = "6.28318530717958647692528676655900577";
329        const FRAC_PI_2: &str = "1.57079632679489661923132169163975144";
330        const FRAC_PI_3: &str = "1.04719755119659774615421446109316763";
331        const FRAC_PI_4: &str = "0.785398163397448309615660845819875721";
332        const FRAC_PI_6: &str = "0.52359877559829887307710723054658381";
333        const FRAC_PI_8: &str = "0.39269908169872415480783042290993786";
334        const FRAC_1_PI: &str = "0.318309886183790671537767526745028724";
335        const FRAC_2_PI: &str = "0.636619772367581343075535053490057448";
336        const FRAC_2_SQRT_PI: &str = "1.12837916709551257389615890312154517";
337        const SQRT_2: &str = "1.41421356237309504880168872420969808";
338        const FRAC_1_SQRT_2: &str = "0.707106781186547524400844362104849039";
339        const E: &str = "2.71828182845904523536028747135266250";
340        const LOG2_10: &str = "3.32192809488736234787031942948939018";
341        const LOG2_E: &str = "1.44269504088896340735992468100189214";
342        const LOG10_2: &str = "0.301029995663981195213738894724493027";
343        const LOG10_E: &str = "0.434294481903251827651128918916605082";
344        const LN_2: &str = "0.693147180559945309417232121458176568";
345        const LN_10: &str = "2.30258509299404568401799145468436421";
346
347        is_eq_f(Bitstring64::PI, PI);
348        is_eq_f(Bitstring64::TAU, TAU);
349        is_eq_f(Bitstring64::FRAC_PI_2, FRAC_PI_2);
350        is_eq_f(Bitstring64::FRAC_PI_3, FRAC_PI_3);
351        is_eq_f(Bitstring64::FRAC_PI_4, FRAC_PI_4);
352        is_eq_f(Bitstring64::FRAC_PI_6, FRAC_PI_6);
353        is_eq_f(Bitstring64::FRAC_PI_8, FRAC_PI_8);
354        is_eq_f(Bitstring64::FRAC_1_PI, FRAC_1_PI);
355        is_eq_f(Bitstring64::FRAC_2_PI, FRAC_2_PI);
356        is_eq_f(Bitstring64::FRAC_2_SQRT_PI, FRAC_2_SQRT_PI);
357        is_eq_f(Bitstring64::SQRT_2, SQRT_2);
358        is_eq_f(Bitstring64::FRAC_1_SQRT_2, FRAC_1_SQRT_2);
359        is_eq_f(Bitstring64::E, E);
360        is_eq_f(Bitstring64::LOG2_10, LOG2_10);
361        is_eq_f(Bitstring64::LOG2_E, LOG2_E);
362        is_eq_f(Bitstring64::LOG10_2, LOG10_2);
363        is_eq_f(Bitstring64::LOG10_E, LOG10_E);
364        is_eq_f(Bitstring64::LN_2, LN_2);
365        is_eq_f(Bitstring64::LN_10, LN_10);
366
367        // NOTE: 10e-15 according to https://en.wikipedia.org/wiki/Machine_epsilon#cite_note-2
368        is_eq(
369            Bitstring64::EPSILON,
370            Bitstring64::from_str("0.00000000000001").unwrap(),
371        );
372        is_eq(Bitstring64::MIN, Bitstring64::min());
373        is_eq(Bitstring64::MIN_POSITIVE, Bitstring64::min_positive());
374        is_eq(Bitstring64::MAX, Bitstring64::max());
375        is_eq(Bitstring64::NAN, Bitstring64::from_str("nan").unwrap());
376        is_eq(Bitstring64::INFINITY, Bitstring64::from_str("inf").unwrap());
377        is_eq(
378            Bitstring64::NEG_INFINITY,
379            Bitstring64::from_str("-inf").unwrap(),
380        );
381
382        assert_eq!(
383            Bitstring64::MIN_10_EXP,
384            crate::binary::emin::<i32>(64) - (Bitstring64::DIGITS as i32) + 1
385        );
386        assert_eq!(
387            Bitstring64::MAX_10_EXP,
388            crate::binary::emax::<i32>(64) - (Bitstring64::DIGITS as i32) + 1
389        );
390    }
391}