dec/
decimal32.rs

1// Copyright Materialize, Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License in the LICENSE file at the
6// root of this repository, or online at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use std::convert::TryFrom;
17use std::ffi::{CStr, CString};
18use std::fmt;
19use std::marker::PhantomData;
20use std::mem::MaybeUninit;
21use std::str::FromStr;
22
23use libc::c_char;
24
25use crate::context::Context;
26use crate::decimal::Decimal;
27use crate::decimal64::Decimal64;
28use crate::error::ParseDecimalError;
29
30/// A 32-bit decimal floating-point number.
31#[repr(transparent)]
32#[derive(Clone, Copy)]
33pub struct Decimal32 {
34    pub(crate) inner: decnumber_sys::decSingle,
35}
36
37impl Decimal32 {
38    /// The value that represents Not-a-Number (NaN).
39    pub const NAN: Decimal32 = Decimal32::from_ne_bytes(if cfg!(target_endian = "little") {
40        [0x0, 0x0, 0x0, 0x7c]
41    } else {
42        [0x7c, 0x0, 0x0, 0x0]
43    });
44
45    /// The value that represents zero.
46    pub const ZERO: Decimal32 = Decimal32::from_ne_bytes(if cfg!(target_endian = "little") {
47        [0x0, 0x0, 0x50, 0x22]
48    } else {
49        [0x22, 0x50, 0x0, 0x0]
50    });
51
52    /// The value that represents one.
53    pub const ONE: Decimal32 = Decimal32::from_ne_bytes(if cfg!(target_endian = "little") {
54        [0x1, 0x0, 0x50, 0x22]
55    } else {
56        [0x22, 0x50, 0x0, 0x1]
57    });
58
59    /// Creates a number from its representation as a little-endian byte array.
60    pub fn from_le_bytes(mut bytes: [u8; 4]) -> Decimal32 {
61        if cfg!(target_endian = "big") {
62            bytes.reverse();
63        }
64        Decimal32::from_ne_bytes(bytes)
65    }
66
67    /// Creates a number from its representation as a big-endian byte array.
68    pub fn from_be_bytes(mut bytes: [u8; 4]) -> Decimal32 {
69        if cfg!(target_endian = "little") {
70            bytes.reverse();
71        }
72        Decimal32::from_ne_bytes(bytes)
73    }
74
75    /// Creates a number from its representation as a byte array in the
76    /// native endianness of the target platform.
77    pub const fn from_ne_bytes(bytes: [u8; 4]) -> Decimal32 {
78        Decimal32 {
79            inner: decnumber_sys::decSingle { bytes },
80        }
81    }
82
83    /// Returns the memory representation of the number as a byte array in
84    /// little-endian order.
85    pub fn to_le_bytes(&self) -> [u8; 4] {
86        let mut bytes = self.to_ne_bytes();
87        if cfg!(target_endian = "big") {
88            bytes.reverse();
89        }
90        bytes
91    }
92
93    /// Returns the memory representation of the number as a byte array in
94    /// big-endian order.
95    pub fn to_be_bytes(&self) -> [u8; 4] {
96        let mut bytes = self.to_ne_bytes();
97        if cfg!(target_endian = "little") {
98            bytes.reverse();
99        }
100        bytes
101    }
102
103    /// Returns the memory representation of the number as a byte array in
104    /// the native endianness of the target platform.
105    pub fn to_ne_bytes(&self) -> [u8; 4] {
106        self.inner.bytes
107    }
108
109    /// Computes the coefficient of the number.
110    ///
111    /// If the number is a special value (i.e., NaN or infinity), returns zero.
112    pub fn coefficient(&self) -> i32 {
113        let mut dpd = if cfg!(target_endian = "big") {
114            u32::from_be_bytes(self.inner.bytes)
115        } else {
116            u32::from_le_bytes(self.inner.bytes)
117        };
118
119        if dpd == 0 {
120            return 0;
121        }
122
123        // Check if first bit is 1, indicating val is negative; this is equal to
124        // 2^31
125        let is_neg = dpd >= 2_147_483_648;
126
127        // Densely packed decimals are 10-bit strings.
128        let dpd_mask = 0b11_1111_1111;
129
130        // Digits 2-7
131        let mut r =
132            i32::try_from(unsafe { decnumber_sys::DPD2BIN[dpd as usize & dpd_mask] }).unwrap();
133        dpd >>= 10;
134        r += i32::try_from(unsafe { decnumber_sys::DPD2BINK[dpd as usize & dpd_mask] }).unwrap();
135
136        // Digit 1
137        let h = i32::try_from(unsafe { decnumber_sys::DECCOMBMSD[(dpd >> 16) as usize] }).unwrap();
138
139        if h > 0 {
140            r += h * 1_000_000;
141        }
142
143        if is_neg {
144            r *= -1;
145        }
146
147        r
148    }
149
150    /// Computes the exponent of the number.
151    pub fn exponent(&self) -> i32 {
152        unsafe { decnumber_sys::decSingleGetExponent(&self.inner) }
153    }
154}
155
156impl Default for Decimal32 {
157    fn default() -> Decimal32 {
158        Decimal32::ZERO
159    }
160}
161
162impl fmt::Debug for Decimal32 {
163    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164        fmt::Display::fmt(self, f)
165    }
166}
167
168impl fmt::Display for Decimal32 {
169    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170        let mut buf = ['\0'; decnumber_sys::DECDOUBLE_String];
171        let c_str = unsafe {
172            if f.alternate() {
173                decnumber_sys::decSingleToEngString(&self.inner, buf.as_mut_ptr() as *mut c_char);
174            } else {
175                decnumber_sys::decSingleToString(&self.inner, buf.as_mut_ptr() as *mut c_char);
176            }
177            CStr::from_ptr(buf.as_ptr() as *const c_char)
178        };
179        f.write_str(
180            c_str
181                .to_str()
182                .expect("decSingleToString yields valid UTF-8"),
183        )
184    }
185}
186
187impl FromStr for Decimal32 {
188    type Err = ParseDecimalError;
189
190    fn from_str(s: &str) -> Result<Decimal32, ParseDecimalError> {
191        Context::<Decimal32>::default().parse(s)
192    }
193}
194
195impl Default for Context<Decimal32> {
196    fn default() -> Context<Decimal32> {
197        let mut ctx = MaybeUninit::<decnumber_sys::decContext>::uninit();
198        let ctx = unsafe {
199            decnumber_sys::decContextDefault(ctx.as_mut_ptr(), decnumber_sys::DEC_INIT_DECDOUBLE);
200            ctx.assume_init()
201        };
202        Context {
203            inner: ctx,
204            _phantom: PhantomData,
205        }
206    }
207}
208
209impl Context<Decimal32> {
210    /// Parses a number from its string representation.
211    pub fn parse<S>(&mut self, s: S) -> Result<Decimal32, ParseDecimalError>
212    where
213        S: Into<Vec<u8>>,
214    {
215        let c_string = CString::new(s).map_err(|_| ParseDecimalError)?;
216        let mut d = Decimal32::ZERO;
217        unsafe {
218            decnumber_sys::decSingleFromString(&mut d.inner, c_string.as_ptr(), &mut self.inner);
219        }
220        if (self.inner.status & decnumber_sys::DEC_Conversion_syntax) != 0 {
221            Err(ParseDecimalError)
222        } else {
223            Ok(d)
224        }
225    }
226
227    /// Constructs a number from a 64-bit decimal float.
228    ///
229    /// The result may be inexact. The status fields on the context will be set
230    /// appropriately if so.
231    pub fn from_decimal64(&mut self, d64: Decimal64) -> Decimal32 {
232        let mut d32 = Decimal32::ZERO;
233        unsafe {
234            decnumber_sys::decSingleFromWider(&mut d32.inner, &d64.inner, &mut self.inner);
235        }
236        d32
237    }
238
239    /// Constructs a number from an arbitrary-precision decimal.
240    ///
241    /// The result may be inexact. The status fields on the context will be set
242    /// appropriately if so.
243    pub fn from_decimal<const N: usize>(&mut self, d: &Decimal<N>) -> Decimal32 {
244        let mut d32 = Decimal32::ZERO;
245        unsafe {
246            decnumber_sys::decimal32FromNumber(&mut d32.inner, d.as_ptr(), &mut self.inner);
247        }
248        d32
249    }
250}