Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-compute/src/decimal.rs
7884 views
1
/*
2
Decimal implementation.
3
4
Throughout this module it's assumed that p and s fit in the maximum precision,
5
giving panics otherwise.
6
7
Constants for division have been generated with the following Python code:
8
9
# Finds integer (c, s) such that floor(n / d) == (n * c) >> s for all n in [0, N].
10
# From Integer division by constants: optimal bounds by Lemire et. al, Theorem 1.
11
# This constant allows for fast division, as well as a divisibility check,
12
# namely we find that n % d == 0 for all n in [0, N] iff (n * c) % 2**s < c.
13
def inv_mult_shift(d, N):
14
s = 0
15
m = 1
16
c = 1
17
K = N - (N + 1) % d
18
while True:
19
if c * d * K < (1 + K)*m:
20
break
21
s += 1
22
m *= 2
23
c = (m + d - 1) // d # ceil(m / d)
24
return (c, s)
25
26
Also from that paper is the algorithm we use for round-to-nearest division.
27
We compute z = n + floor(d/2), and then return floor(z / d) unless z % d == 0
28
and the result is odd in which case we subtract 1.
29
*/
30
31
use std::cmp::Ordering;
32
33
use polars_error::{PolarsResult, polars_ensure};
34
35
/// The maximum precision of a Decimal128.
36
pub const DEC128_MAX_PREC: usize = 38;
37
38
pub fn dec128_verify_prec_scale(p: usize, s: usize) -> PolarsResult<()> {
39
polars_ensure!((1..=DEC128_MAX_PREC).contains(&p), InvalidOperation: "precision must be between 1 and 38");
40
polars_ensure!(s <= p, InvalidOperation: "scale must be less than or equal to precision");
41
Ok(())
42
}
43
44
pub const POW10_I128: &[i128; 39] = &{
45
let mut out = [0; 39];
46
let mut i = 0;
47
while i < 39 {
48
out[i] = 10_i128.pow(i as u32);
49
i += 1;
50
}
51
out
52
};
53
54
pub const POW10_F64: &[f64; 39] = &{
55
let mut out = [0.0; 39];
56
let mut i = 0;
57
while i < 39 {
58
out[i] = POW10_I128[i] as f64;
59
i += 1;
60
}
61
out
62
};
63
64
// for e in range(39):
65
// c, s = inv_mult_shift(10**e, 2**127-1)
66
#[rustfmt::skip]
67
const POW10_127_INV_MUL: &[u128; 39] = &[
68
0x00000000000000000000000000000001, 0x66666666666666666666666666666667,
69
0x28f5c28f5c28f5c28f5c28f5c28f5c29, 0x4189374bc6a7ef9db22d0e5604189375,
70
0x68db8bac710cb295e9e1b089a0275255, 0x29f16b11c6d1e108c3f3e0370cdc8755,
71
0x08637bd05af6c69b5a63f9a49c2c1b11, 0xd6bf94d5e57a42bc3d32907604691b4d,
72
0x55e63b88c230e77e7ee106959b5d3e1f, 0x89705f4136b4a59731680a88f8953031,
73
0x36f9bfb3af7b756fad5cd10396a21347, 0x57f5ff85e592557f7bc7b4d28a9ceba5,
74
0x232f33025bd42232fe4fe1edd10b9175, 0x709709a125da07099432d2f9035837dd,
75
0xb424dc35095cd80f538484c19ef38c95, 0x901d7cf73ab0acd90f9d37014bf60a11,
76
0x39a5652fb1137856d30baf9a1e626a6d, 0x2e1dea8c8da92d12426fbfae7eb521f1,
77
0x09392ee8e921d5d073aff322e62439fd, 0x760f253edb4ab0d29598f4f1e8361973,
78
0xbce5086492111aea88f4bb1ca6bcf585, 0x25c768141d369efbb4fdbf05baf29781,
79
0xf1c90080baf72cb15324c68b12dd6339, 0x305b66802564a289dd6dc14f03c5e0a5,
80
0x9abe14cd44753b52c4926a9672793543, 0x7bcb43d769f762a89d41eedec1fa9103,
81
0x63090312bb2c4eed4a9b257f019540cf, 0x4f3a68dbc8f03f243baf513267aa9a3f,
82
0xfd87b5f28300ca0d8bca9d6e188853fd, 0xcad2f7f5359a3b3e096ee45813a04331,
83
0x51212ffbaf0a7e18d092c1bcd4a68147, 0x1039d66589687f9e901d59f290ee19db,
84
0xcfb11ead453994ba67de18eda5814af3, 0xa6274bbdd0fadd61ecb1ad8aeacdd58f,
85
0x84ec3c97da624ab4bd5af13bef0b113f, 0xd4ad2dbfc3d07787955e4ec64b44e865,
86
0x5512124cb4b9c9696ef285e8eae85cf5, 0x881cea14545c75757e50d64177da2e55,
87
0x6ce3ee76a9e3912acb73de9ac6482511,
88
];
89
90
const POW10_127_SHIFT: &[u8; 39] = &[
91
0, 2, 4, 8, 12, 14, 15, 23, 25, 29, 31, 35, 37, 42, 46, 49, 51, 54, 55, 62, 66, 67, 73, 74, 79,
92
82, 85, 88, 93, 96, 98, 99, 106, 109, 112, 116, 118, 122, 125,
93
];
94
95
// for e in range(39):
96
// c, s = inv_mult_shift(10**e, 2**255-1)
97
#[rustfmt::skip]
98
const POW10_255_INV_MUL: &[U256; 39] = &[
99
U256([0x0000000000000001, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000]),
100
U256([0x6666666666666667, 0x6666666666666666, 0x6666666666666666, 0x6666666666666666]),
101
U256([0xae147ae147ae147b, 0x7ae147ae147ae147, 0x47ae147ae147ae14, 0x147ae147ae147ae1]),
102
U256([0x5604189374bc6a7f, 0x4bc6a7ef9db22d0e, 0xdb22d0e560418937, 0x04189374bc6a7ef9]),
103
U256([0x19ce075f6fd21ff3, 0x305532617c1bda51, 0xf4f0d844d013a92a, 0x346dc5d63886594a]),
104
U256([0x85c67dfe32a0663d, 0xcddd6e04c0592103, 0x0fcf80dc33721d53, 0xa7c5ac471b478423]),
105
U256([0x37d1fe64f54d1e97, 0xd7e45803cd141a69, 0xa63f9a49c2c1b10f, 0x8637bd05af6c69b5]),
106
U256([0xc6419850c43db213, 0x4650466970dce1ed, 0x1e99483b02348da6, 0x6b5fca6af2bd215e]),
107
U256([0x7068f3b46d2f8351, 0x3d4d3d758161697c, 0xfdc20d2b36ba7c3d, 0xabcc77118461cefc]),
108
U256([0xf387295d242602a7, 0xfdd7645e011abac9, 0x31680a88f8953030, 0x89705f4136b4a597]),
109
U256([0x2e36108ba80f3443, 0xcbefc1bf33a44ab7, 0xad5cd10396a21346, 0x36f9bfb3af7b756f]),
110
U256([0x49f01a790ce5206b, 0x797f9c651f6d4458, 0x7bc7b4d28a9ceba4, 0x57f5ff85e592557f]),
111
U256([0x4319c3f4e16e9a45, 0xf598fa3b657ba08d, 0xf93f87b7442e45d3, 0x8cbccc096f5088cb]),
112
U256([0x409ec0ca937c8541, 0x311e9872477f201c, 0x650cb4be40d60df7, 0x1c25c268497681c2]),
113
U256([0x01fc02883e5b4403, 0x36c84e3a7e6399f4, 0xa9c24260cf79c64a, 0x5a126e1a84ae6c07]),
114
U256([0x3660040d3092066b, 0x57a6e390ca38f653, 0x0f9d37014bf60a10, 0x901d7cf73ab0acd9]),
115
U256([0x48f334d2136d9c2b, 0xefdc5b06b749fc21, 0xd30baf9a1e626a6c, 0x39a5652fb1137856]),
116
U256([0x4fd70f6d0af85a23, 0xff8df0157db98d37, 0x09befeb9fad487c2, 0xb877aa3236a4b449]),
117
U256([0x8656062b9dfcf0db, 0x996bf9a2324a387c, 0x9d7f99173121cfe7, 0x49c97747490eae83]),
118
U256([0xe11346f1f98fcf89, 0x1e2652070753e7f4, 0x2b31e9e3d06c32e5, 0xec1e4a7db69561a5]),
119
U256([0x26d482c7309fec9d, 0x0c0f5402cfbb2995, 0x447a5d8e535e7ac2, 0x5e72843249088d75]),
120
U256([0xa48737a51a997a95, 0x467eecd14c5ea8ee, 0xd3f6fc16ebca5e03, 0x971da05074da7bee]),
121
U256([0x1d38f950e2146211, 0x38658a4109e553f2, 0xa9926345896eb19c, 0x78e480405d7b9658]),
122
U256([0x05d831dcfa04139d, 0x71ade873686110ca, 0xeeb6e0a781e2f052, 0x182db34012b25144]),
123
U256([0xf23472530ce6e3ed, 0xd78c3615cf3a050c, 0xc4926a9672793542, 0x9abe14cd44753b52]),
124
U256([0xe9ed83b814a49fe1, 0x8c1389bc7ec33b47, 0x3a83ddbd83f52204, 0xf79687aed3eec551]),
125
U256([0x87f1362cdd507fe7, 0x3cdc6e306568fc39, 0x95364afe032a819d, 0xc612062576589dda]),
126
U256([0xcffa15ab8bb9ccc3, 0xe524f8e0289064e3, 0x3baf513267aa9a3e, 0x4f3a68dbc8f03f24]),
127
U256([0xe65cef78df8fae05, 0x3b6e5b0040e707d2, 0xc5e54eb70c4429fe, 0x7ec3daf941806506]),
128
U256([0x28f1f9638c9fdf35, 0x17c5be0019f60321, 0x825bb91604e810cc, 0x32b4bdfd4d668ecf]),
129
U256([0x3b6398471c1ff971, 0xd18df2ccd1fe00a0, 0x1a1258379a94d028, 0x0a2425ff75e14fc3]),
130
U256([0x7c1701c71a663c6d, 0xa38c78520cc00401, 0x407567ca43b8676b, 0x40e7599625a1fe7a]),
131
U256([0x59e338e387ad8e29, 0x0b5b1aa028ccd99e, 0x67de18eda5814af2, 0xcfb11ead453994ba]),
132
U256([0x11fa3e93e7ef82d5, 0x9bdf05533b5c2b86, 0x7b2c6b62bab37563, 0x2989d2ef743eb758]),
133
U256([0xe990641fd97f37bb, 0x5fcb3bb85ef9df3c, 0x5ead789df785889f, 0x42761e4bed31255a]),
134
U256([0x90a0280cbd66164b, 0x8cb7b17cf2ca594b, 0xf2abc9d8c9689d0c, 0x1a95a5b7f87a0ef0]),
135
U256([0xb4337347957023ab, 0x7abf82618476f545, 0xb77942f475742e7a, 0x2a8909265a5ce4b4]),
136
U256([0x40a4a418449a0bbd, 0xbbfe6e04db164412, 0x7e50d64177da2e54, 0x881cea14545c7575]),
137
U256([0x735420d1a7520259, 0x259949342bd140d0, 0xb2dcf7a6b1920944, 0x1b38fb9daa78e44a]),
138
];
139
140
const POW10_255_SHIFT: &[u8; 39] = &[
141
0, 2, 3, 4, 11, 16, 19, 22, 26, 29, 31, 35, 39, 40, 45, 49, 51, 56, 58, 63, 65, 69, 72, 73, 79,
142
83, 86, 88, 92, 94, 95, 101, 106, 107, 111, 113, 117, 122, 123,
143
];
144
145
// Limbs in little-endian order (limb 0 is least significant).
146
#[derive(Copy, Clone, PartialEq, Eq)]
147
struct U256([u64; 4]);
148
149
impl U256 {
150
#[inline(always)]
151
fn from_lo_hi(lo: u128, hi: u128) -> Self {
152
Self([lo as u64, (lo >> 64) as u64, hi as u64, (hi >> 64) as u64])
153
}
154
}
155
156
impl PartialOrd for U256 {
157
#[inline(always)]
158
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
159
Some(self.cmp(other))
160
}
161
}
162
163
impl Ord for U256 {
164
#[inline(always)]
165
fn cmp(&self, other: &Self) -> Ordering {
166
self.0[3]
167
.cmp(&other.0[3])
168
.then(self.0[2].cmp(&other.0[2]))
169
.then(self.0[1].cmp(&other.0[1]))
170
.then(self.0[0].cmp(&other.0[0]))
171
}
172
}
173
174
#[inline]
175
fn u128_from_lo_hi(lo: u64, hi: u64) -> u128 {
176
(lo as u128) | ((hi as u128) << 64)
177
}
178
179
#[inline(always)]
180
fn widening_mul_64(a: u64, b: u64) -> (u64, u64) {
181
let t = (a as u128) * (b as u128);
182
(t as u64, (t >> 64) as u64)
183
}
184
185
#[inline(always)]
186
fn carrying_add_64(a: u64, b: u64, carry: bool) -> (u64, bool) {
187
let (t0, c1) = a.overflowing_add(b);
188
let (t1, c2) = t0.overflowing_add(carry as u64);
189
(t1, c1 | c2)
190
}
191
192
#[inline]
193
fn widening_mul_128(a: u128, b: u128) -> (u128, u128) {
194
let a_lo = a as u64;
195
let a_hi = (a >> 64) as u64;
196
let b_lo = b as u64;
197
let b_hi = (b >> 64) as u64;
198
let (x0, x1) = widening_mul_64(a_lo, b_lo);
199
let (y1, y2) = widening_mul_64(a_hi, b_lo);
200
let (z1, z2) = widening_mul_64(a_lo, b_hi);
201
let (w2, w3) = widening_mul_64(a_hi, b_hi);
202
203
let mut out = [0; 4];
204
let (c1, c2, c3, c4);
205
out[0] = x0;
206
(out[1], c1) = carrying_add_64(x1, y1, false);
207
(out[1], c2) = carrying_add_64(out[1], z1, false);
208
(out[2], c3) = carrying_add_64(y2, z2, c1);
209
(out[2], c4) = carrying_add_64(out[2], w2, c2);
210
out[3] = w3.wrapping_add(c3 as u64 + c4 as u64);
211
212
(
213
out[0] as u128 | ((out[1] as u128) << 64),
214
out[2] as u128 | ((out[3] as u128) << 64),
215
)
216
}
217
218
fn widening_mul_256(a: U256, b: U256) -> (U256, U256) {
219
let mut out = [0; 8];
220
221
// Algorithm M from TAOCP: Seminumerical algorithms, ch 4.3.1.
222
// We represent the carry as carry_word + c1 + c2, which fits in a u64.
223
for i in 0..4 {
224
let mut carry_word = 0;
225
let mut c1 = false;
226
let mut c2 = false;
227
for j in 0..4 {
228
let (mut lo, hi) = widening_mul_64(a.0[i], b.0[j]);
229
(lo, c1) = carrying_add_64(lo, out[i + j], c1);
230
(lo, c2) = carrying_add_64(lo, carry_word, c2);
231
out[i + j] = lo;
232
carry_word = hi;
233
}
234
out[i + 4] = carry_word + c1 as u64 + c2 as u64;
235
}
236
237
let (lo, hi) = out.split_at(4);
238
(U256(lo.try_into().unwrap()), U256(hi.try_into().unwrap()))
239
}
240
241
/// Returns x * 10^e, with e <= DEC128_MAX_PREC.
242
///
243
/// Returns None if the multiplication overflows.
244
#[inline]
245
fn mul_128_pow10(x: i128, e: usize) -> Option<i128> {
246
x.checked_mul(POW10_I128[e])
247
}
248
249
/// Returns round(x / 10^e), with e <= DEC128_MAX_PREC, rounding to nearest even.
250
#[inline]
251
fn div_128_pow10(x: i128, e: usize) -> i128 {
252
if e == 0 {
253
return x;
254
}
255
256
let n = x.unsigned_abs();
257
let z = n + ((POW10_I128[e] as u128) / 2); // Can't overflow.
258
let c = POW10_127_INV_MUL[e];
259
let s = POW10_127_SHIFT[e];
260
let (lo, hi) = widening_mul_128(z, c);
261
let mut ret = (hi >> s) as i128;
262
if lo < c && ret % 2 == 1 && (hi << (128 - s)) == 0 {
263
ret -= 1;
264
}
265
if x < 0 { -ret } else { ret }
266
}
267
268
/// Returns round(x / 10^e), with e <= DEC128_MAX_PREC, rounding to nearest even.
269
/// x is assumed to be < 2^255. Returns None if the result doesn't fit in a u128.
270
#[inline]
271
fn div_255_pow10(x: U256, e: usize) -> Option<u128> {
272
if e == 0 {
273
if x.0[2] == 0 && x.0[3] == 0 {
274
return Some(u128_from_lo_hi(x.0[0], x.0[1]));
275
} else {
276
return None;
277
}
278
}
279
280
let half = (POW10_I128[e] as u128) / 2;
281
let mut carry;
282
let mut z = x;
283
(z.0[0], carry) = z.0[0].overflowing_add(half as u64);
284
(z.0[1], carry) = carrying_add_64(z.0[1], (half >> 64) as u64, carry);
285
(z.0[2], carry) = z.0[2].overflowing_add(carry as u64);
286
z.0[3] += carry as u64;
287
let c = POW10_255_INV_MUL[e];
288
let s = POW10_255_SHIFT[e];
289
let (lo, hi) = widening_mul_256(z, c);
290
let shifted_out_is_zero;
291
let mut ret = if s < 64 {
292
if (hi.0[2] >> s) != 0 || hi.0[3] != 0 {
293
return None;
294
}
295
shifted_out_is_zero = (hi.0[0] << (64 - s)) == 0;
296
(u128_from_lo_hi(hi.0[0], hi.0[1]) >> s) | u128_from_lo_hi(0, hi.0[2] << (64 - s))
297
} else {
298
debug_assert!(s < 128);
299
let s = s - 64;
300
if (hi.0[3] >> s) != 0 {
301
return None;
302
}
303
shifted_out_is_zero = hi.0[0] == 0 && (hi.0[1] << (64 - s)) == 0;
304
(u128_from_lo_hi(hi.0[1], hi.0[2]) >> s) | u128_from_lo_hi(0, hi.0[3] << (64 - s))
305
};
306
307
if lo < c && ret % 2 == 1 && shifted_out_is_zero {
308
ret -= 1;
309
}
310
311
Some(ret)
312
}
313
314
/// Calculates n / d, returning quotient and remainder.
315
///
316
/// # Safety
317
/// Assumes quotient fits in u64, and d != 0.
318
unsafe fn divrem_128_64(n: u128, d: u64) -> (u64, u64) {
319
let quo: u64;
320
let rem: u64;
321
322
#[cfg(target_arch = "x86_64")]
323
unsafe {
324
let nlo = n as u64;
325
let nhi = (n >> 64) as u64;
326
std::arch::asm!(
327
"div {d}",
328
d = in(reg) d,
329
inlateout("rax") nlo => quo,
330
inlateout("rdx") nhi => rem,
331
options(pure, nomem, nostack)
332
);
333
}
334
335
#[cfg(not(target_arch = "x86_64"))]
336
unsafe {
337
// TODO: more optimized implementation.
338
if n < (1 << 64) {
339
quo = (n as u64).checked_div(d).unwrap_unchecked();
340
rem = (n as u64).checked_rem(d).unwrap_unchecked();
341
} else {
342
quo = n.checked_div(d as u128).unwrap_unchecked() as u64;
343
rem = n.checked_rem(d as u128).unwrap_unchecked() as u64;
344
}
345
}
346
347
(quo, rem)
348
}
349
350
/// Calculates the quotient and remainder of ((hi << 128) | lo) / d.
351
/// Returns None if the quotient overflows a 128-bit integer.
352
fn divrem_256_128(lo: u128, hi: u128, d: u128) -> Option<(u128, u128)> {
353
if d == 0 || hi >= d {
354
return None;
355
}
356
357
if hi == 0 {
358
return Some(((lo / d), (lo % d)));
359
}
360
361
if d < (1 << 64) {
362
// Short division (exercise 16, TAOCP, 4.3.1).
363
let d = d as u64;
364
let (q_hi, r_hi) =
365
unsafe { divrem_128_64(u128_from_lo_hi((lo >> 64) as u64, hi as u64), d) };
366
let (q_lo, r_lo) = unsafe { divrem_128_64(u128_from_lo_hi(lo as u64, r_hi), d) };
367
return Some((u128_from_lo_hi(q_lo, q_hi), u128_from_lo_hi(r_lo, 0)));
368
}
369
370
// Long division (algorithm D, TAOCP, 4.3.1).
371
// Normalize d, n so that d has the top bit set.
372
let shift = ((d >> 64) as u64).leading_zeros();
373
let d1 = ((d << shift) >> 64) as u64;
374
let d0 = (d as u64) << shift;
375
let mut n3 = (hi >> 64) as u64;
376
let mut n2 = hi as u64;
377
let mut n1 = (lo >> 64) as u64;
378
let mut n0 = lo as u64;
379
n3 = ((u128_from_lo_hi(n2, n3) << shift) >> 64) as u64;
380
n2 = ((u128_from_lo_hi(n1, n2) << shift) >> 64) as u64;
381
n1 = ((u128_from_lo_hi(n0, n1) << shift) >> 64) as u64;
382
n0 <<= shift;
383
384
// We want to calculate
385
// (qhat, rhat) = divmod(n3n2, d1)
386
// and then do the test qhat * d0 > (rhat << 64) + n1, possibly twice, to
387
// adjust qhat downwards. But we have to be very careful around overflow,
388
// as both the division and intermediate steps can overflow.
389
let (mut qhat, mut rhat, mut qhd0_lo, mut qhd0_hi, mut borrow);
390
if n3 < d1 {
391
(qhat, rhat) = unsafe { divrem_128_64(u128_from_lo_hi(n2, n3), d1) };
392
(qhd0_lo, qhd0_hi) = widening_mul_64(qhat, d0);
393
} else {
394
qhat = 0; // Represents 1 << 64, will be corrected below.
395
rhat = n2;
396
qhd0_lo = 0;
397
qhd0_hi = d0;
398
};
399
400
if qhd0_hi > rhat || qhd0_hi == rhat && qhd0_lo > n1 {
401
qhat = qhat.wrapping_sub(1);
402
let rhat_overflow;
403
(rhat, rhat_overflow) = rhat.overflowing_add(d1);
404
(qhd0_lo, borrow) = qhd0_lo.overflowing_sub(d0);
405
qhd0_hi -= borrow as u64;
406
if !rhat_overflow && (qhd0_hi > rhat || qhd0_hi == rhat && qhd0_lo > n1) {
407
qhat -= 1;
408
(qhd0_lo, borrow) = qhd0_lo.overflowing_sub(d0);
409
qhd0_hi -= borrow as u64;
410
}
411
}
412
413
// Subtract qhat*d from n3n2n1, this zeroes out n3. We don't need to worry
414
// about our number going negative like in the original Algorithm D because
415
// we only have two limbs worth of divisor (making qhat exact).
416
let q_hi = qhat;
417
n2 = n2.wrapping_sub(qhat.wrapping_mul(d1));
418
(n1, borrow) = n1.overflowing_sub(qhd0_lo);
419
n2 = n2.wrapping_sub(qhd0_hi + borrow as u64);
420
421
// Repeat the whole process again with n2n1n0.
422
if n2 < d1 {
423
(qhat, rhat) = unsafe { divrem_128_64(u128_from_lo_hi(n1, n2), d1) };
424
(qhd0_lo, qhd0_hi) = widening_mul_64(qhat, d0);
425
} else {
426
qhat = 0; // Represents 1 << 64, will be corrected below.
427
rhat = n1;
428
qhd0_lo = 0;
429
qhd0_hi = d0;
430
};
431
432
if qhd0_hi > rhat || qhd0_hi == rhat && qhd0_lo > n0 {
433
qhat = qhat.wrapping_sub(1);
434
let rhat_overflow;
435
(rhat, rhat_overflow) = rhat.overflowing_add(d1);
436
(qhd0_lo, borrow) = qhd0_lo.overflowing_sub(d0);
437
qhd0_hi -= borrow as u64;
438
if !rhat_overflow && (qhd0_hi > rhat || qhd0_hi == rhat && qhd0_lo > n0) {
439
qhat -= 1;
440
(qhd0_lo, borrow) = qhd0_lo.overflowing_sub(d0);
441
qhd0_hi -= borrow as u64;
442
}
443
}
444
445
let q_lo = qhat;
446
n1 = n1.wrapping_sub(qhat.wrapping_mul(d1));
447
(n0, borrow) = n0.overflowing_sub(qhd0_lo);
448
n1 = n1.wrapping_sub(qhd0_hi + borrow as u64);
449
450
// n1n0 is now our remainder, once we account for the shift.
451
let r_lo = (u128_from_lo_hi(n0, n1) >> shift) as u64;
452
let r_hi = n1 >> shift;
453
454
Some((u128_from_lo_hi(q_lo, q_hi), u128_from_lo_hi(r_lo, r_hi)))
455
}
456
457
/// Returns whether the given Decimal128 fits in the given precision.
458
#[inline]
459
pub fn dec128_fits(x: i128, p: usize) -> bool {
460
x.abs() < POW10_I128[p]
461
}
462
463
#[inline]
464
pub fn dec128_to_i128(x: i128, s: usize) -> i128 {
465
if s == 0 { x } else { div_128_pow10(x, s) }
466
}
467
468
/// Converts an i128 to a Decimal128 with the given precision and scale,
469
/// returning None if the value doesn't fit.
470
#[inline]
471
pub fn i128_to_dec128(x: i128, p: usize, s: usize) -> Option<i128> {
472
let r = x.checked_mul(POW10_I128[s])?;
473
dec128_fits(r, p).then_some(r)
474
}
475
476
/// Converts a Decimal128 with the given scale to a f64.
477
#[inline]
478
pub fn dec128_to_f64(x: i128, s: usize) -> f64 {
479
// TODO: correctly rounded result. This rounds multiple times.
480
x as f64 / POW10_F64[s]
481
}
482
483
/// Converts a f64 to a Decimal128 with the given precision and scale, returning
484
/// None if the value doesn't fit.
485
#[inline]
486
pub fn f64_to_dec128(x: f64, p: usize, s: usize) -> Option<i128> {
487
// TODO: correctly rounded result. This rounds multiple times.
488
#[allow(clippy::neg_cmp_op_on_partial_ord)]
489
if !(x.abs() < POW10_F64[p]) {
490
// Comparison will fail for NaN, making us return None.
491
return None;
492
}
493
unsafe { Some((x * POW10_F64[s]).round_ties_even().to_int_unchecked()) }
494
}
495
496
/// Converts between two Decimal128s, with a new precision and scale, returning
497
/// None if the value doesn't fit.
498
#[inline]
499
pub fn dec128_rescale(x: i128, old_s: usize, new_p: usize, new_s: usize) -> Option<i128> {
500
let r = if new_s < old_s {
501
div_128_pow10(x, old_s - new_s)
502
} else if new_s > old_s {
503
mul_128_pow10(x, new_s - old_s)?
504
} else {
505
return Some(x);
506
};
507
508
dec128_fits(r, new_p).then_some(r)
509
}
510
511
/// Adds two Decimal128s, assuming they have the same scale.
512
#[inline]
513
pub fn dec128_add(l: i128, r: i128, p: usize) -> Option<i128> {
514
l.checked_add(r).filter(|x| dec128_fits(*x, p))
515
}
516
517
/// Subs two Decimal128s, assuming they have the same scale.
518
#[inline]
519
pub fn dec128_sub(l: i128, r: i128, p: usize) -> Option<i128> {
520
l.checked_sub(r).filter(|x| dec128_fits(*x, p))
521
}
522
523
/// Multiplies two Decimal128s, assuming they have the same scale s.
524
#[inline]
525
pub fn dec128_mul(l: i128, r: i128, p: usize, s: usize) -> Option<i128> {
526
// Computes round(l * r / 10^s), rounding to nearest even.
527
if let (Ok(ls), Ok(rs)) = (i64::try_from(l), i64::try_from(r)) {
528
// Fast path, both small.
529
let ret = div_128_pow10(ls as i128 * rs as i128, s);
530
dec128_fits(ret, p).then_some(ret)
531
} else {
532
let negative = (l < 0) ^ (r < 0);
533
let lu = l.unsigned_abs();
534
let ru = r.unsigned_abs();
535
536
let (lo, hi) = widening_mul_128(lu, ru);
537
let retu = if hi == 0 && lo <= i128::MAX as u128 {
538
div_128_pow10(lo as i128, s) as u128
539
} else {
540
div_255_pow10(U256::from_lo_hi(lo, hi), s)?
541
};
542
if retu >= POW10_I128[p] as u128 {
543
return None;
544
}
545
if negative {
546
Some(-(retu as i128))
547
} else {
548
Some(retu as i128)
549
}
550
}
551
}
552
553
/// Divides two Decimal128s, assuming they have the same scale s.
554
#[inline]
555
pub fn dec128_div(l: i128, r: i128, p: usize, s: usize) -> Option<i128> {
556
if r == 0 {
557
return None;
558
}
559
560
let negative = (l < 0) ^ (r < 0);
561
let lu = l.unsigned_abs();
562
let ru = r.unsigned_abs();
563
564
// Computes round((l / r) * 10^s), rounding to nearest even.
565
let (mut retu, rem) = if s == 0 {
566
// Fast path, integer division.
567
let z = lu + ru / 2; // Can't overflow, 10^38 + 10^38 / 2 < 2^128.
568
(z / ru, z % ru)
569
} else {
570
let m = POW10_I128[s];
571
572
if let (Ok(ls), Ok(ms)) = (i64::try_from(l), u64::try_from(m)) {
573
// Fast path, intermediate product representable as u128.
574
let lsu = ls.unsigned_abs();
575
let mut tmp = lsu as u128 * ms as u128;
576
tmp += ru / 2; // Checked that adding this can't overflow, assuming l < 2^63 and m, r < POW10_I128[DEC128_MAX_PREC].
577
(tmp / ru, tmp % ru)
578
} else {
579
let (mut lo, mut hi) = widening_mul_128(lu, m as u128);
580
let carry;
581
(lo, carry) = lo.overflowing_add(ru / 2);
582
hi += carry as u128;
583
divrem_256_128(lo, hi, ru)?
584
}
585
};
586
587
// Round to nearest even.
588
if r % 2 == 0 && retu % 2 == 1 && rem == 0 {
589
retu -= 1;
590
}
591
592
if retu >= POW10_I128[p] as u128 {
593
return None;
594
}
595
if negative {
596
Some(-(retu as i128))
597
} else {
598
Some(retu as i128)
599
}
600
}
601
602
/// Checks if two Decimal128s are equal in value.
603
#[inline]
604
pub fn dec128_eq(mut lv: i128, ls: usize, mut rv: i128, rs: usize) -> bool {
605
// Rescale to largest scale. If this overflows the numbers can't be equal anyway.
606
if ls < rs {
607
let Some(scaled_lv) = mul_128_pow10(lv, rs - ls) else {
608
return false;
609
};
610
lv = scaled_lv;
611
} else if ls > rs {
612
let Some(scaled_rv) = mul_128_pow10(rv, ls - rs) else {
613
return false;
614
};
615
rv = scaled_rv;
616
}
617
618
lv == rv
619
}
620
621
/// Checks how two Decimal128s compare.
622
#[inline]
623
pub fn dec128_cmp(mut lv: i128, ls: usize, mut rv: i128, rs: usize) -> Ordering {
624
// Rescale to largest scale. If this overflows we know the magnitude of the
625
// (attempted) rescaled number is larger and we can resolve the answer just
626
// using its sign.
627
if ls < rs {
628
let Some(scaled_lv) = mul_128_pow10(lv, rs - ls) else {
629
return if lv < 0 {
630
Ordering::Less
631
} else {
632
Ordering::Greater
633
};
634
};
635
lv = scaled_lv;
636
} else if ls > rs {
637
let Some(scaled_rv) = mul_128_pow10(rv, ls - rs) else {
638
return if 0 < rv {
639
Ordering::Less
640
} else {
641
Ordering::Greater
642
};
643
};
644
rv = scaled_rv;
645
}
646
647
lv.cmp(&rv)
648
}
649
650
/// Get the sign (either -1, 0, 1) of a Decimal128.
651
#[inline]
652
pub fn dec128_sign(x: i128, s: usize) -> i128 {
653
if x < 0 {
654
-POW10_I128[s]
655
} else if x > 0 {
656
POW10_I128[s]
657
} else {
658
0
659
}
660
}
661
662
/// Deserialize bytes to a single i128 representing a decimal, at a specified
663
/// precision and scale. The number is checked to ensure it fits within the
664
/// specified precision and scale. Consistent with float parsing, no decimal
665
/// separator is required (eg "500", "500.", and "500.0" are all accepted);
666
/// this allows mixed integer/decimal sequences to be parsed as decimals.
667
/// Returns None if the number is not well-formed, or does not fit. The decimal
668
/// separator defaults to b'.', but can be changed to b',' by setting `decimal_comma`
669
/// to true.
670
pub fn str_to_dec128(bytes: &[u8], p: usize, s: usize, decimal_comma: bool) -> Option<i128> {
671
assert!(dec128_verify_prec_scale(p, s).is_ok());
672
let exp_pos = bytes
673
.iter()
674
.position(|b| *b == b'e' || *b == b'E')
675
.unwrap_or(bytes.len());
676
let (num_bytes, exp_bytes) = bytes.split_at(exp_pos);
677
let decimal_sep = if decimal_comma { b',' } else { b'.' };
678
let sep_pos = num_bytes
679
.iter()
680
.position(|b| *b == decimal_sep)
681
.unwrap_or(num_bytes.len());
682
let (mut int_bytes, mut frac_bytes) = num_bytes.split_at(sep_pos);
683
684
if frac_bytes.is_empty() && exp_bytes.is_empty() {
685
// Integer-only fast path.
686
let n: i128 = atoi_simd::parse_skipped(int_bytes).ok()?;
687
return i128_to_dec128(n, p, s);
688
}
689
690
// Skip sign and separator to get clean integers.
691
let negative = match int_bytes.first() {
692
Some(sign @ (b'+' | b'-')) => {
693
int_bytes = &int_bytes[1..];
694
*sign == b'-'
695
},
696
_ => false,
697
};
698
699
if !frac_bytes.is_empty() {
700
frac_bytes = &frac_bytes[1..];
701
}
702
703
if frac_bytes.is_empty() && int_bytes.is_empty() {
704
// No digits at all.
705
return None;
706
}
707
708
let exp_scale: i32 = if !exp_bytes.is_empty() {
709
atoi_simd::parse_skipped(&exp_bytes[1..]).ok()?
710
} else {
711
0
712
};
713
714
// Round if digits extend beyond the scale.
715
let comb_scale = exp_scale + s as i32;
716
let (next_digit, all_zero_after);
717
let (int_scale, frac_scale) = if comb_scale < 0 {
718
let int_part_len = int_bytes.len().saturating_sub((-comb_scale) as usize);
719
if !int_bytes[int_part_len..]
720
.iter()
721
.chain(frac_bytes)
722
.all(|b| b.is_ascii_digit())
723
{
724
return None;
725
}
726
if (-comb_scale) as usize > int_bytes.len() {
727
// All digits are valid (so no error), but also irrelevant.
728
return Some(0);
729
}
730
731
next_digit = int_bytes[int_part_len];
732
all_zero_after = int_bytes[int_part_len + 1..]
733
.iter()
734
.chain(frac_bytes)
735
.all(|b| *b == b'0');
736
int_bytes = &int_bytes[..int_part_len];
737
frac_bytes = &[];
738
(0, 0)
739
} else if frac_bytes.len() > comb_scale as usize {
740
let cs = comb_scale as usize;
741
if !frac_bytes[cs..].iter().all(|b| b.is_ascii_digit()) {
742
return None;
743
}
744
next_digit = frac_bytes[cs];
745
all_zero_after = frac_bytes[cs + 1..].iter().all(|b| *b == b'0');
746
frac_bytes = &frac_bytes[..cs];
747
(cs, 0)
748
} else {
749
let cs = comb_scale as usize;
750
next_digit = b'0';
751
all_zero_after = true;
752
(cs, cs - frac_bytes.len())
753
};
754
755
// Parse parts.
756
// Workaround atoi_simd/issues/14.
757
while let Some((b'0', rest)) = int_bytes.split_first() {
758
int_bytes = rest;
759
}
760
while let Some((b'0', rest)) = frac_bytes.split_first() {
761
frac_bytes = rest;
762
}
763
764
let mut pint: i128 = if int_bytes.is_empty() {
765
0
766
} else {
767
atoi_simd::parse_pos(int_bytes).ok()?
768
};
769
770
let mut pfrac: i128 = if frac_bytes.is_empty() {
771
0
772
} else {
773
atoi_simd::parse_pos(frac_bytes).ok()?
774
};
775
776
// Round-to-even.
777
if next_digit > b'5' || next_digit == b'5' && !all_zero_after {
778
pfrac += 1;
779
} else if next_digit == b'5' {
780
if comb_scale <= 0 {
781
pint += pint % 2;
782
} else {
783
pfrac += pfrac % 2;
784
}
785
}
786
787
// Apply scales.
788
if pint > 0 {
789
if int_scale > DEC128_MAX_PREC {
790
return None;
791
}
792
pint = mul_128_pow10(pint, int_scale)?;
793
}
794
795
if pfrac > 0 {
796
if frac_scale > DEC128_MAX_PREC {
797
return None;
798
}
799
pfrac = mul_128_pow10(pfrac, frac_scale)?;
800
}
801
802
let ret = pint + pfrac;
803
if !dec128_fits(ret, p) {
804
return None;
805
}
806
if negative { Some(-ret) } else { Some(ret) }
807
}
808
809
const DEC128_MAX_LEN: usize = 39 + 2;
810
811
#[derive(Clone, Copy)]
812
pub struct DecimalFmtBuffer {
813
data: [u8; DEC128_MAX_LEN],
814
len: usize,
815
}
816
817
impl Default for DecimalFmtBuffer {
818
fn default() -> Self {
819
Self::new()
820
}
821
}
822
823
impl DecimalFmtBuffer {
824
#[inline]
825
pub const fn new() -> Self {
826
Self {
827
data: [0; DEC128_MAX_LEN],
828
len: 0,
829
}
830
}
831
832
pub fn format_dec128(
833
&mut self,
834
x: i128,
835
scale: usize,
836
trim_zeros: bool,
837
decimal_comma: bool,
838
) -> &str {
839
let decimal_sep = if decimal_comma { b',' } else { b'.' };
840
841
let mut itoa_buf = itoa::Buffer::new();
842
let xs = itoa_buf.format(x.unsigned_abs()).as_bytes();
843
844
if x >= 0 {
845
self.len = 0;
846
} else {
847
self.data[0] = b'-';
848
self.len = 1;
849
}
850
851
if scale == 0 {
852
self.data[self.len..self.len + xs.len()].copy_from_slice(xs);
853
self.len += xs.len();
854
} else {
855
let whole_len = xs.len().saturating_sub(scale);
856
let frac_len = xs.len() - whole_len;
857
if whole_len == 0 {
858
self.data[self.len] = b'0';
859
self.data[self.len + 1] = decimal_sep;
860
self.data[self.len + 2..self.len + 2 + scale - frac_len].fill(b'0');
861
self.len += 2 + scale - frac_len;
862
} else {
863
self.data[self.len..self.len + whole_len].copy_from_slice(&xs[..whole_len]);
864
self.data[self.len + whole_len] = decimal_sep;
865
self.len += whole_len + 1;
866
}
867
868
self.data[self.len..self.len + frac_len].copy_from_slice(&xs[whole_len..]);
869
self.len += frac_len;
870
871
if trim_zeros {
872
while self.data.get(self.len - 1) == Some(&b'0') {
873
self.len -= 1;
874
}
875
if self.data.get(self.len - 1) == Some(&decimal_sep) {
876
self.len -= 1;
877
}
878
}
879
}
880
881
unsafe { std::str::from_utf8_unchecked(&self.data[..self.len]) }
882
}
883
}
884
885
#[cfg(test)]
886
mod test {
887
use std::sync::LazyLock;
888
889
use bigdecimal::{BigDecimal, RoundingMode};
890
use num_bigint::BigInt;
891
use num_traits::Signed;
892
use polars_utils::aliases::PlHashSet;
893
use rand::prelude::*;
894
895
use super::*;
896
897
fn bigdecimal_to_dec128(x: &BigDecimal, p: usize, s: usize) -> Option<i128> {
898
let n = x
899
.with_scale_round(s as i64, RoundingMode::HalfEven)
900
.into_bigint_and_scale()
901
.0;
902
if n.abs() < POW10_I128[p].into() {
903
Some(n.try_into().unwrap())
904
} else {
905
None
906
}
907
}
908
909
fn dec128_to_bigdecimal(x: i128, s: usize) -> BigDecimal {
910
BigDecimal::from_bigint(BigInt::from(x), s as i64)
911
}
912
913
static INTERESTING_SCALE_PREC: [usize; 13] = [0, 1, 2, 3, 5, 8, 11, 16, 21, 27, 32, 37, 38];
914
915
static INTERESTING_VALUES: LazyLock<Vec<BigDecimal>> = LazyLock::new(|| {
916
let mut r = SmallRng::seed_from_u64(42);
917
let mut base = Vec::new();
918
base.extend((0..128).map(|e| BigDecimal::from(1i128 << e)));
919
base.extend((0..39).map(|e| BigDecimal::from(POW10_I128[e])));
920
base.extend((0..32).map(BigDecimal::from));
921
base.extend((0..32).map(|_| BigDecimal::from(r.random::<u32>())));
922
base.extend((0..32).map(|_| BigDecimal::from(r.random::<u64>())));
923
base.extend((0..32).map(|_| BigDecimal::from(r.random::<u128>())));
924
base.extend(base.clone().into_iter().map(|x| -x));
925
926
let mut out = PlHashSet::default();
927
out.extend(base.iter().cloned());
928
929
let zero = BigDecimal::from(0u8);
930
for l in &base {
931
for r in &base {
932
out.insert(l + r);
933
out.insert(l * r);
934
if *r != zero {
935
out.insert(l / r);
936
}
937
}
938
}
939
940
let mut out: Vec<_> = out.into_iter().collect();
941
out.sort_by_key(|d| d.abs());
942
out
943
});
944
945
#[test]
946
#[rustfmt::skip]
947
fn test_str_to_dec() {
948
fn str_to_dec128_dot(bytes: &[u8], p: usize, s: usize) -> Option<i128> {
949
str_to_dec128(bytes, p, s, false)
950
}
951
952
assert_eq!(str_to_dec128_dot(b"12.09", 8, 2), Some(1209));
953
assert_eq!(str_to_dec128_dot(b"1200.90", 8, 2), Some(120090));
954
assert_eq!(str_to_dec128_dot(b"143.9", 8, 2), Some(14390));
955
956
assert_eq!(str_to_dec128_dot(b"+000000.5", 8, 2), Some(50));
957
assert_eq!(str_to_dec128_dot(b"-0.5", 8, 2), Some(-50));
958
assert_eq!(str_to_dec128_dot(b"-1.5", 8, 2), Some(-150));
959
960
assert_eq!(str_to_dec128_dot(b"12ABC.34", 8, 5), None);
961
assert_eq!(str_to_dec128_dot(b"1ABC2.34", 8, 5), None);
962
assert_eq!(str_to_dec128_dot(b"12.3ABC4", 8, 5), None);
963
assert_eq!(str_to_dec128_dot(b"12.3.ABC4", 8, 5), None);
964
965
assert_eq!(str_to_dec128_dot(b"12.-3", 8, 5), None);
966
assert_eq!(str_to_dec128_dot(b"", 8, 5), None);
967
assert_eq!(str_to_dec128_dot(b"5.", 8, 5), Some(500000i128));
968
assert_eq!(str_to_dec128_dot(b"5", 8, 5), Some(500000i128));
969
assert_eq!(str_to_dec128_dot(b".5", 8, 5), Some(50000i128));
970
971
// Precision and scale fitting.
972
let val = b"1200";
973
assert_eq!(str_to_dec128_dot(val, 4, 0), Some(1200));
974
assert_eq!(str_to_dec128_dot(val, 3, 0), None);
975
assert_eq!(str_to_dec128_dot(val, 4, 1), None);
976
977
let val = b"1200.010";
978
assert_eq!(str_to_dec128_dot(val, 7, 0), Some(1200));
979
assert_eq!(str_to_dec128_dot(val, 7, 3), Some(1200010));
980
assert_eq!(str_to_dec128_dot(val, 10, 6), Some(1200010000));
981
assert_eq!(str_to_dec128_dot(val, 5, 3), None);
982
assert_eq!(str_to_dec128_dot(val, 12, 5), Some(120001000));
983
assert_eq!(str_to_dec128_dot(val, 38, 35), None);
984
985
// Rounding.
986
assert_eq!(str_to_dec128_dot(b"2.10", 5, 1), Some(21));
987
assert_eq!(str_to_dec128_dot(b"2.14", 5, 1), Some(21));
988
assert_eq!(str_to_dec128_dot(b"2.15", 5, 1), Some(22));
989
assert_eq!(str_to_dec128_dot(b"2.24", 5, 1), Some(22));
990
assert_eq!(str_to_dec128_dot(b"2.25", 5, 1), Some(22));
991
assert_eq!(str_to_dec128_dot(b"2.26", 5, 1), Some(23));
992
993
assert_eq!(str_to_dec128_dot(b"-.6", 5, 0), Some(-1));
994
assert_eq!(str_to_dec128_dot(b"-.5", 5, 0), Some(0));
995
assert_eq!(str_to_dec128_dot(b"-.4", 5, 0), Some(0));
996
assert_eq!(str_to_dec128_dot(b"-.0", 5, 0), Some(0));
997
assert_eq!(str_to_dec128_dot(b"-0", 5, 0), Some(0));
998
assert_eq!(str_to_dec128_dot(b".4", 5, 0), Some(0));
999
assert_eq!(str_to_dec128_dot(b".5", 5, 0), Some(0));
1000
assert_eq!(str_to_dec128_dot(b".6", 5, 0), Some(1));
1001
1002
// Rounding + scientific.
1003
assert_eq!(str_to_dec128_dot(b"-6e-1", 5, 0), Some(-1));
1004
assert_eq!(str_to_dec128_dot(b"-5E-0001", 5, 0), Some(0));
1005
assert_eq!(str_to_dec128_dot(b"-4e-0000001", 5, 0), Some(0));
1006
assert_eq!(str_to_dec128_dot(b"0E-1", 5, 0), Some(0));
1007
assert_eq!(str_to_dec128_dot(b"4.e-1", 5, 0), Some(0));
1008
assert_eq!(str_to_dec128_dot(b"5.E-1", 5, 0), Some(0));
1009
assert_eq!(str_to_dec128_dot(b"6.e-1", 5, 0), Some(1));
1010
assert_eq!(str_to_dec128_dot(b"-.6e0", 5, 0), Some(-1));
1011
assert_eq!(str_to_dec128_dot(b"-.5e+0", 5, 0), Some(0));
1012
assert_eq!(str_to_dec128_dot(b"-.4e-0", 5, 0), Some(0));
1013
assert_eq!(str_to_dec128_dot(b"-.0e000", 5, 0), Some(0));
1014
assert_eq!(str_to_dec128_dot(b"-0e000000000", 5, 0), Some(0));
1015
assert_eq!(str_to_dec128_dot(b".4e0", 5, 0), Some(0));
1016
assert_eq!(str_to_dec128_dot(b".5e000", 5, 0), Some(0));
1017
assert_eq!(str_to_dec128_dot(b".6e0", 5, 0), Some(1));
1018
assert_eq!(str_to_dec128_dot(b"-.06e+1", 5, 0), Some(-1));
1019
assert_eq!(str_to_dec128_dot(b"-.05E+0001", 5, 0), Some(0));
1020
assert_eq!(str_to_dec128_dot(b"-.04e1", 5, 0), Some(0));
1021
assert_eq!(str_to_dec128_dot(b".0E+1", 5, 0), Some(0));
1022
assert_eq!(str_to_dec128_dot(b".004e02", 5, 0), Some(0));
1023
assert_eq!(str_to_dec128_dot(b".005E000002", 5, 0), Some(0));
1024
assert_eq!(str_to_dec128_dot(b".006e+2", 5, 0), Some(1));
1025
1026
// Other scientific.
1027
assert_eq!(str_to_dec128_dot(b"600e-2", 5, 0), Some(6));
1028
assert_eq!(str_to_dec128_dot(b"600e-2", 5, 1), Some(60));
1029
assert_eq!(str_to_dec128_dot(b"600e-2", 5, 2), Some(600));
1030
assert_eq!(str_to_dec128_dot(b"600e-2", 5, 3), Some(6000));
1031
assert_eq!(str_to_dec128_dot(b"600e-2", 5, 4), Some(60000));
1032
assert_eq!(str_to_dec128_dot(b"1600e-3", 5, 4), Some(16000));
1033
assert_eq!(str_to_dec128_dot(b"1600e-2", 5, 4), None);
1034
assert_eq!(str_to_dec128_dot(b"123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-125", 10, 6), Some(1234560));
1035
1036
assert_eq!(str_to_dec128_dot(b"99999999999999999999999999999999999999", 38, 0), Some(99999999999999999999999999999999999999));
1037
assert_eq!(str_to_dec128_dot(b"99999999999999999999999999999999999999e0", 38, 0), Some(99999999999999999999999999999999999999));
1038
assert_eq!(str_to_dec128_dot(b"999999999999999999999999999999999999990e-1", 38, 0), Some(99999999999999999999999999999999999999));
1039
assert_eq!(str_to_dec128_dot(b"999999999999999999999999999999999999994e-1", 38, 0), Some(99999999999999999999999999999999999999));
1040
assert_eq!(str_to_dec128_dot(b"999999999999999999999999999999999999995e-1", 38, 0), None);
1041
assert_eq!(str_to_dec128_dot(b"99999999999999999999999999999999999999.12345", 38, 0), Some(99999999999999999999999999999999999999));
1042
assert_eq!(str_to_dec128_dot(b"99999999999999999999999999999999999999.12345e0", 38, 0), Some(99999999999999999999999999999999999999));
1043
assert_eq!(str_to_dec128_dot(b"999999999999999999999999999999999999990.e-1", 38, 0), Some(99999999999999999999999999999999999999));
1044
assert_eq!(str_to_dec128_dot(b"999999999999999999999999999999999999994.99999e-1", 38, 0), Some(99999999999999999999999999999999999999));
1045
assert_eq!(str_to_dec128_dot(b"999999999999999999999999999999999999995.00000e-1", 38, 0), None);
1046
assert_eq!(str_to_dec128_dot( b"00.0000000000000000000000000000000000000000000000000000e+50", 5, 0), Some(0));
1047
1048
// Decimal comma.
1049
assert_eq!(str_to_dec128(b"12,09", 8, 2, true), Some(1209));
1050
assert_eq!(str_to_dec128(b"1200,90", 8, 2, true), Some(120090));
1051
assert_eq!(str_to_dec128(b"143,9", 8, 2, true), Some(14390));
1052
1053
// Edge-cases around missing components.
1054
assert_eq!(str_to_dec128_dot(b"0", 8, 2), Some(0));
1055
assert_eq!(str_to_dec128_dot(b"-0", 8, 2), Some(0));
1056
assert_eq!(str_to_dec128_dot(b"0.", 8, 2), Some(0));
1057
assert_eq!(str_to_dec128_dot(b".0", 8, 2), Some(0));
1058
assert_eq!(str_to_dec128_dot(b"-.0", 8, 2), Some(0));
1059
assert_eq!(str_to_dec128_dot(b"-0.", 8, 2), Some(0));
1060
assert_eq!(str_to_dec128_dot(b"-.", 8, 2), None);
1061
assert_eq!(str_to_dec128_dot(b"-", 8, 2), None);
1062
assert_eq!(str_to_dec128_dot(b".", 8, 2), None);
1063
}
1064
1065
#[test]
1066
fn test_str_to_dec_against_ref() {
1067
let exps = [0, 1, 3, 20, 50, 150];
1068
let digits = [0, 4, 5, 6, 9];
1069
let mut pos_parts = Vec::new();
1070
for leading_zeroes in exps {
1071
let leading = "0".repeat(leading_zeroes);
1072
for trailing_zeroes in exps {
1073
let trailing = "0".repeat(trailing_zeroes);
1074
for a in &digits {
1075
for b in &digits {
1076
pos_parts.push(format!("{leading}{a}{trailing}{b}"));
1077
}
1078
}
1079
}
1080
}
1081
1082
for scale in &INTERESTING_SCALE_PREC {
1083
for int in &pos_parts {
1084
for frac in &pos_parts {
1085
for exp in &exps {
1086
for exp_sign in &["+", "-"] {
1087
let s = format!("{int}.{frac}e{exp_sign}{exp}");
1088
let ours = str_to_dec128(s.as_bytes(), 38, *scale, false);
1089
let theirs = BigDecimal::parse_bytes(s.as_bytes(), 10)
1090
.and_then(|d| bigdecimal_to_dec128(&d, 38, *scale));
1091
assert_eq!(ours, theirs, "input: {s}, scale: {scale}");
1092
}
1093
}
1094
}
1095
}
1096
}
1097
}
1098
1099
#[test]
1100
fn test_str_dec_roundtrip() {
1101
let mut buf = DecimalFmtBuffer::new();
1102
for &p in &INTERESTING_SCALE_PREC {
1103
for &s in &INTERESTING_SCALE_PREC {
1104
if s > p || p == 0 {
1105
continue;
1106
}
1107
for x in INTERESTING_VALUES.iter() {
1108
for d_comma in [true, false] {
1109
if let Some(d) = bigdecimal_to_dec128(x, p, s) {
1110
let fmt = buf.format_dec128(d, s, false, d_comma);
1111
let d2 = str_to_dec128(fmt.as_bytes(), p, s, d_comma);
1112
assert_eq!(d, d2.unwrap());
1113
} else {
1114
break;
1115
}
1116
}
1117
}
1118
}
1119
}
1120
}
1121
1122
#[test]
1123
fn test_mul() {
1124
for &p in &INTERESTING_SCALE_PREC {
1125
for &s in &INTERESTING_SCALE_PREC {
1126
if s > p || p == 0 {
1127
continue;
1128
}
1129
let values: Vec<_> = INTERESTING_VALUES
1130
.iter()
1131
.map_while(|x| bigdecimal_to_dec128(x, p, s))
1132
.map(|d| (d, dec128_to_bigdecimal(d, s)))
1133
.collect();
1134
let mut r = SmallRng::seed_from_u64(42);
1135
for _ in 0..1_000 {
1136
// Kept small for CI, ran with 10 million during development.
1137
let (x, xb) = values.choose(&mut r).unwrap();
1138
let (y, yb) = values.choose(&mut r).unwrap();
1139
let prod = dec128_mul(*x, *y, p, s);
1140
let prodb = bigdecimal_to_dec128(&(xb * yb), p, s);
1141
assert_eq!(prod, prodb);
1142
}
1143
}
1144
}
1145
}
1146
1147
#[test]
1148
fn test_div() {
1149
for &p in &INTERESTING_SCALE_PREC {
1150
for &s in &INTERESTING_SCALE_PREC {
1151
if s > p || p == 0 {
1152
continue;
1153
}
1154
let values: Vec<_> = INTERESTING_VALUES
1155
.iter()
1156
.map_while(|x| bigdecimal_to_dec128(x, p, s))
1157
.map(|d| (d, dec128_to_bigdecimal(d, s)))
1158
.collect();
1159
let mut r = SmallRng::seed_from_u64(42);
1160
for _ in 0..1_000 {
1161
// Kept small for CI, ran with 10 million during development.
1162
let (x, xb) = values.choose(&mut r).unwrap();
1163
let (y, yb) = values.choose(&mut r).unwrap();
1164
if *y == 0 {
1165
assert!(dec128_div(*x, *y, p, s).is_none());
1166
continue;
1167
}
1168
let prod = dec128_mul(*x, *y, p, s);
1169
let prodb = bigdecimal_to_dec128(&(xb * yb), p, s);
1170
assert_eq!(prod, prodb);
1171
}
1172
}
1173
}
1174
}
1175
}
1176
1177