1use std::fmt;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct DecFloat {
16 negative: bool,
17 kind: DecKind,
18 coefficient: u128,
20 exponent: i32,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24enum DecKind {
25 Finite,
26 Infinity,
27 NaN,
29}
30
31impl DecFloat {
32 pub fn from_decimal64(bytes: [u8; 8]) -> DecFloat {
35 decode(u64::from_be_bytes(bytes) as u128, 64)
36 }
37
38 pub fn from_decimal128(bytes: [u8; 16]) -> DecFloat {
40 decode(u128::from_be_bytes(bytes), 128)
41 }
42
43 pub fn is_negative(&self) -> bool {
45 self.negative
46 }
47
48 pub fn is_finite(&self) -> bool {
50 self.kind == DecKind::Finite
51 }
52
53 pub fn is_nan(&self) -> bool {
55 self.kind == DecKind::NaN
56 }
57
58 pub fn is_infinite(&self) -> bool {
60 self.kind == DecKind::Infinity
61 }
62
63 pub fn to_parts(&self) -> Option<(bool, u128, i32)> {
66 self.is_finite()
67 .then_some((self.negative, self.coefficient, self.exponent))
68 }
69
70 pub fn from_parts(negative: bool, coefficient: u128, exponent: i32) -> DecFloat {
72 DecFloat {
73 negative,
74 kind: DecKind::Finite,
75 coefficient,
76 exponent,
77 }
78 }
79
80 pub fn infinity(negative: bool) -> DecFloat {
82 DecFloat {
83 negative,
84 kind: DecKind::Infinity,
85 coefficient: 0,
86 exponent: 0,
87 }
88 }
89
90 pub fn nan() -> DecFloat {
92 DecFloat {
93 negative: false,
94 kind: DecKind::NaN,
95 coefficient: 0,
96 exponent: 0,
97 }
98 }
99
100 pub fn to_decimal64(&self) -> Option<[u8; 8]> {
104 Some((self.encode_bits(64)? as u64).to_be_bytes())
105 }
106
107 pub fn to_decimal128(&self) -> Option<[u8; 16]> {
109 Some(self.encode_bits(128)?.to_be_bytes())
110 }
111
112 fn encode_bits(&self, width: u32) -> Option<u128> {
113 match self.kind {
114 DecKind::Finite => encode(self.negative, self.coefficient, self.exponent, width),
115 DecKind::Infinity => Some(special_bits(self.negative, 0b1_1110, width)),
116 DecKind::NaN => Some(special_bits(self.negative, 0b1_1111, width)),
117 }
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
123pub struct ParseDecFloatError;
124
125impl fmt::Display for ParseDecFloatError {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 f.write_str("string DECFLOAT inválida")
128 }
129}
130
131impl std::error::Error for ParseDecFloatError {}
132
133impl std::str::FromStr for DecFloat {
134 type Err = ParseDecFloatError;
135
136 fn from_str(s: &str) -> Result<DecFloat, ParseDecFloatError> {
139 let t = s.trim();
140 let (negative, body) = match t.strip_prefix('-') {
141 Some(rest) => (true, rest),
142 None => (false, t.strip_prefix('+').unwrap_or(t)),
143 };
144 match body.to_ascii_lowercase().as_str() {
145 "inf" | "infinity" => return Ok(DecFloat::infinity(negative)),
146 "nan" => return Ok(DecFloat::nan()),
147 "" => return Err(ParseDecFloatError),
148 _ => {}
149 }
150 let (mantissa, exp_part) = match body.split_once(['e', 'E']) {
152 Some((m, e)) => (m, e.parse::<i32>().map_err(|_| ParseDecFloatError)?),
153 None => (body, 0),
154 };
155 let (int_part, frac_part) = match mantissa.split_once('.') {
156 Some((i, f)) => (i, f),
157 None => (mantissa, ""),
158 };
159 if int_part.is_empty() && frac_part.is_empty() {
160 return Err(ParseDecFloatError);
161 }
162 let mut digits = String::with_capacity(int_part.len() + frac_part.len());
163 digits.push_str(int_part);
164 digits.push_str(frac_part);
165 if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
166 return Err(ParseDecFloatError);
167 }
168 let coefficient: u128 = digits.parse().map_err(|_| ParseDecFloatError)?;
169 let exponent = exp_part - frac_part.len() as i32;
170 Ok(DecFloat::from_parts(negative, coefficient, exponent))
171 }
172}
173
174fn special_bits(negative: bool, combo: u128, width: u32) -> u128 {
176 ((negative as u128) << (width - 1)) | (combo << (width - 6))
177}
178
179fn encode(negative: bool, coefficient: u128, exponent: i32, width: u32) -> Option<u128> {
182 let (ecbits, declets, bias) = match width {
183 64 => (8u32, 5u32, 398i32),
184 _ => (12u32, 11u32, 6176i32), };
186 let total_digits = (3 * declets + 1) as usize;
187 let digits = coefficient.to_string();
188 if digits.len() > total_digits {
189 return None; }
191 let biased = exponent.checked_add(bias)?;
192 let max_biased = (1i32 << (ecbits + 2)) - 1;
193 if !(0..=max_biased).contains(&biased) {
194 return None; }
196 let biased = biased as u32;
197
198 let mut padded = vec![0u8; total_digits - digits.len()];
200 padded.extend(digits.bytes().map(|b| b - b'0'));
201
202 let msd = padded[0] as u32;
203 let exp_top2 = (biased >> ecbits) & 0b11;
204 let econ = biased & ((1 << ecbits) - 1);
205 let combo = if msd <= 7 {
206 (exp_top2 << 3) | msd
207 } else {
208 0b1_1000 | (exp_top2 << 1) | (msd & 1)
209 };
210
211 let mut bits: u128 = 0;
212 bits |= (negative as u128) << (width - 1);
213 bits |= (combo as u128) << (width - 6);
214 bits |= (econ as u128) << (width - 6 - ecbits);
215 for i in 0..declets as usize {
216 let g = &padded[1 + i * 3..1 + i * 3 + 3];
217 let dpd = bcd_to_dpd(g[0] as u16, g[1] as u16, g[2] as u16) as u128;
218 let bit = ((declets as usize - 1 - i) * 10) as u32;
219 bits |= dpd << bit;
220 }
221 Some(bits)
222}
223
224impl fmt::Display for DecFloat {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 match self.kind {
227 DecKind::Infinity => f.write_str(if self.negative {
228 "-Infinity"
229 } else {
230 "Infinity"
231 }),
232 DecKind::NaN => f.write_str("NaN"),
233 DecKind::Finite => {
234 if self.negative {
235 f.write_str("-")?;
236 }
237 f.write_str(&render_finite(self.coefficient, self.exponent))
238 }
239 }
240 }
241}
242
243fn render_finite(coefficient: u128, exponent: i32) -> String {
246 let digits = coefficient.to_string();
247 if exponent >= 0 {
248 let mut s = digits;
250 s.extend(std::iter::repeat_n('0', exponent as usize));
251 s
252 } else {
253 let frac = (-exponent) as usize;
254 if digits.len() > frac {
255 let point = digits.len() - frac;
257 format!("{}.{}", &digits[..point], &digits[point..])
258 } else {
259 let zeros = frac - digits.len();
261 format!("0.{}{}", "0".repeat(zeros), digits)
262 }
263 }
264}
265
266fn decode(bits: u128, width: u32) -> DecFloat {
268 let (ecbits, declets, bias) = match width {
269 64 => (8u32, 5u32, 398i32),
270 _ => (12u32, 11u32, 6176i32), };
272 let negative = (bits >> (width - 1)) & 1 == 1;
273 let combo = ((bits >> (width - 6)) & 0x1F) as u32;
274
275 let (msd, exp_top2) = if combo >> 3 == 0b11 {
278 if (combo >> 1) & 0b1111 == 0b1111 {
279 let kind = if combo & 1 == 0 {
281 DecKind::Infinity
282 } else {
283 DecKind::NaN
284 };
285 return DecFloat {
286 negative,
287 kind,
288 coefficient: 0,
289 exponent: 0,
290 };
291 }
292 (8 + (combo & 1), (combo >> 1) & 0b11)
294 } else {
295 (combo & 0b111, (combo >> 3) & 0b11)
297 };
298
299 let econ = ((bits >> (width - 6 - ecbits)) & ((1u128 << ecbits) - 1)) as u32;
300 let biased_exp = ((exp_top2 << ecbits) | econ) as i32;
301 let exponent = biased_exp - bias;
302
303 let coef_bits = width - 6 - ecbits; let mut coefficient = msd as u128;
306 for d in (0..declets).rev() {
307 let dpd = ((bits >> (d * 10)) & 0x3FF) as u16;
308 debug_assert!((d * 10) < coef_bits);
309 coefficient = coefficient * 1000 + dpd_to_int(dpd) as u128;
310 }
311
312 DecFloat {
313 negative,
314 kind: DecKind::Finite,
315 coefficient,
316 exponent,
317 }
318}
319
320fn dpd_to_int(dpd: u16) -> u16 {
323 DPD_DECODE[dpd as usize & 0x3FF]
324}
325
326static DPD_DECODE: std::sync::LazyLock<[u16; 1024]> = std::sync::LazyLock::new(|| {
330 let mut table = [0u16; 1024];
331 for n in 0u16..1000 {
332 let (d2, d1, d0) = (n / 100, (n / 10) % 10, n % 10);
333 table[bcd_to_dpd(d2, d1, d0) as usize] = n;
334 }
335 table
336});
337
338fn bcd_to_dpd(d2: u16, d1: u16, d0: u16) -> u16 {
341 let (a, b, c, dd) = ((d2 >> 3) & 1, (d2 >> 2) & 1, (d2 >> 1) & 1, d2 & 1);
343 let (e, f, g, h) = ((d1 >> 3) & 1, (d1 >> 2) & 1, (d1 >> 1) & 1, d1 & 1);
344 let (i, j, k, m) = ((d0 >> 3) & 1, (d0 >> 2) & 1, (d0 >> 1) & 1, d0 & 1);
345 let aei = (a << 2) | (e << 1) | i;
346 let (p, q, r, s, t, u, v, x, y, z) = match aei {
348 0b000 => (b, c, dd, f, g, h, 0, j, k, m),
349 0b001 => (b, c, dd, f, g, h, 1, 0, 0, m),
350 0b010 => (b, c, dd, j, k, h, 1, 0, 1, m),
351 0b011 => (b, c, dd, 1, 0, h, 1, 1, 1, m),
352 0b100 => (j, k, dd, f, g, h, 1, 1, 0, m),
353 0b101 => (f, g, dd, 0, 1, h, 1, 1, 1, m),
354 0b110 => (j, k, dd, 0, 0, h, 1, 1, 1, m),
355 _ => (0, 0, dd, 1, 1, h, 1, 1, 1, m), };
357 (p << 9)
358 | (q << 8)
359 | (r << 7)
360 | (s << 6)
361 | (t << 5)
362 | (u << 4)
363 | (v << 3)
364 | (x << 2)
365 | (y << 1)
366 | z
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[test]
374 fn dpd_roundtrip_all_declets() {
375 for n in 0u16..1000 {
377 let (d2, d1, d0) = (n / 100, (n / 10) % 10, n % 10);
378 let dpd = bcd_to_dpd(d2, d1, d0);
379 assert_eq!(dpd_to_int(dpd), n, "falhou para {n}");
380 }
381 }
382
383 #[test]
384 fn dpd_known_anchors() {
385 assert_eq!(bcd_to_dpd(0, 0, 5), 0b00_0000_0101); assert_eq!(bcd_to_dpd(0, 0, 9), 0b00_0000_1001); assert_eq!(bcd_to_dpd(7, 6, 5), 0b11_1110_0101);
391 assert_eq!(bcd_to_dpd(9, 9, 9), 0b00_1111_1111); }
393
394 #[test]
395 fn render_places_decimal_point() {
396 assert_eq!(render_finite(12345, -2), "123.45");
397 assert_eq!(render_finite(100, -2), "1.00");
398 assert_eq!(render_finite(5, -4), "0.0005");
399 assert_eq!(render_finite(5, 3), "5000");
400 assert_eq!(render_finite(0, 0), "0");
401 }
402
403 #[test]
404 fn parse_and_roundtrip_encode_decode() {
405 use std::str::FromStr;
406 for s in [
407 "0",
408 "1",
409 "123.45",
410 "-3.14159",
411 "100.00",
412 "0.0005",
413 "5000",
414 "-0.0",
415 "9999999999999999",
416 ] {
417 let d = DecFloat::from_str(s).unwrap();
418 let back = DecFloat::from_decimal128(d.to_decimal128().unwrap());
420 assert_eq!(back.to_string(), d.to_string(), "decimal128 falhou em {s}");
421 }
422 for s in ["123.45", "-3.14159", "0.0005", "1234567890123456"] {
424 let d = DecFloat::from_str(s).unwrap();
425 let back = DecFloat::from_decimal64(d.to_decimal64().unwrap());
426 assert_eq!(back.to_string(), d.to_string(), "decimal64 falhou em {s}");
427 }
428 }
429
430 #[test]
431 fn parse_scientific_and_specials() {
432 use std::str::FromStr;
433 assert_eq!(DecFloat::from_str("1.5E-3").unwrap().to_string(), "0.0015");
434 assert_eq!(DecFloat::from_str("12E3").unwrap().to_string(), "12000");
435 assert!(DecFloat::from_str("Infinity").unwrap().is_infinite());
436 assert!(DecFloat::from_str("-inf").unwrap().is_infinite());
437 assert!(DecFloat::from_str("NaN").unwrap().is_nan());
438 assert!(DecFloat::from_str("abc").is_err());
439 }
440
441 #[test]
442 fn encode_overflow_returns_none() {
443 let d = DecFloat::from_parts(false, 12_345_678_901_234_567, 0);
445 assert!(d.to_decimal64().is_none());
446 assert!(d.to_decimal128().is_some());
448 }
449
450 #[test]
451 fn encode_specials_roundtrip() {
452 for d in [
453 DecFloat::infinity(false),
454 DecFloat::infinity(true),
455 DecFloat::nan(),
456 ] {
457 let back = DecFloat::from_decimal128(d.to_decimal128().unwrap());
458 assert_eq!(back.is_infinite(), d.is_infinite());
459 assert_eq!(back.is_nan(), d.is_nan());
460 assert_eq!(back.is_negative(), d.is_negative());
461 }
462 }
463
464 #[test]
465 fn decimal128_one() {
466 let bias = 6176u32; let exp_top2 = (bias >> 12) as u128; let econ = (bias & 0xFFF) as u128; let combo = exp_top2 << 3; let last_declet = bcd_to_dpd(0, 0, 1) as u128; let bits = (combo << (128 - 6)) | (econ << (128 - 6 - 12)) | last_declet;
474 let d = decode(bits, 128);
475 assert!(d.is_finite() && !d.is_negative());
476 assert_eq!(d.to_parts(), Some((false, 1, 0)));
477 assert_eq!(d.to_string(), "1");
478 }
479}