parquet_variant/variant/
decimal.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17use arrow_schema::ArrowError;
18use std::fmt;
19
20// All decimal types use the same try_new implementation
21macro_rules! decimal_try_new {
22    ($integer:ident, $scale:ident) => {{
23        // Validate that scale doesn't exceed precision
24        if $scale > Self::MAX_PRECISION {
25            return Err(ArrowError::InvalidArgumentError(format!(
26                "Scale {} is larger than max precision {}",
27                $scale,
28                Self::MAX_PRECISION,
29            )));
30        }
31
32        // Validate that the integer value fits within the precision
33        if $integer.unsigned_abs() > Self::MAX_UNSCALED_VALUE {
34            return Err(ArrowError::InvalidArgumentError(format!(
35                "{} is wider than max precision {}",
36                $integer,
37                Self::MAX_PRECISION
38            )));
39        }
40
41        Ok(Self { $integer, $scale })
42    }};
43}
44
45// All decimal values format the same way, using integer arithmetic to avoid floating point precision loss
46macro_rules! format_decimal {
47    ($f:expr, $integer:expr, $scale:expr, $int_type:ty) => {{
48        let integer = if $scale == 0 {
49            $integer
50        } else {
51            let divisor = <$int_type>::pow(10, $scale as u32);
52            let remainder = $integer % divisor;
53            if remainder != 0 {
54                // Track the sign explicitly, in case the quotient is zero
55                let sign = if $integer < 0 { "-" } else { "" };
56                // Format an unsigned remainder with leading zeros and strip (unnecessary) trailing zeros.
57                let remainder = format!("{:0width$}", remainder.abs(), width = $scale as usize);
58                let remainder = remainder.trim_end_matches('0');
59                let quotient = $integer / divisor;
60                return write!($f, "{}{}.{}", sign, quotient.abs(), remainder);
61            }
62            $integer / divisor
63        };
64        write!($f, "{}", integer)
65    }};
66}
67
68/// Represents a 4-byte decimal value in the Variant format.
69///
70/// This struct stores a decimal number using a 32-bit signed integer for the coefficient
71/// and an 8-bit unsigned integer for the scale (number of decimal places). Its precision is limited to 9 digits.
72///
73/// For valid precision and scale values, see the Variant specification:
74/// <https://github.com/apache/parquet-format/blob/87f2c8bf77eefb4c43d0ebaeea1778bd28ac3609/VariantEncoding.md?plain=1#L418-L420>
75///
76/// # Example: Create a VariantDecimal4
77/// ```
78/// # use parquet_variant::VariantDecimal4;
79/// // Create a value representing the decimal 123.4567
80/// let decimal = VariantDecimal4::try_new(1234567, 4).expect("Failed to create decimal");
81/// ```
82#[derive(Debug, Clone, Copy, PartialEq)]
83pub struct VariantDecimal4 {
84    integer: i32,
85    scale: u8,
86}
87
88impl VariantDecimal4 {
89    pub(crate) const MAX_PRECISION: u8 = 9;
90    pub(crate) const MAX_UNSCALED_VALUE: u32 = u32::pow(10, Self::MAX_PRECISION as u32) - 1;
91
92    pub fn try_new(integer: i32, scale: u8) -> Result<Self, ArrowError> {
93        decimal_try_new!(integer, scale)
94    }
95
96    /// Returns the underlying value of the decimal.
97    ///
98    /// For example, if the decimal is `123.4567`, this will return `1234567`.
99    pub fn integer(&self) -> i32 {
100        self.integer
101    }
102
103    /// Returns the scale of the decimal (how many digits after the decimal point).
104    ///
105    /// For example, if the decimal is `123.4567`, this will return `4`.
106    pub fn scale(&self) -> u8 {
107        self.scale
108    }
109}
110
111impl fmt::Display for VariantDecimal4 {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        format_decimal!(f, self.integer, self.scale, i32)
114    }
115}
116
117/// Represents an 8-byte decimal value in the Variant format.
118///
119/// This struct stores a decimal number using a 64-bit signed integer for the coefficient
120/// and an 8-bit unsigned integer for the scale (number of decimal places). Its precision is between 10 and 18 digits.
121///
122/// For valid precision and scale values, see the Variant specification:
123///
124/// <https://github.com/apache/parquet-format/blob/87f2c8bf77eefb4c43d0ebaeea1778bd28ac3609/VariantEncoding.md?plain=1#L418-L420>
125///
126/// # Example: Create a VariantDecimal8
127/// ```
128/// # use parquet_variant::VariantDecimal8;
129/// // Create a value representing the decimal 123456.78
130/// let decimal = VariantDecimal8::try_new(12345678, 2).expect("Failed to create decimal");
131/// ```
132#[derive(Debug, Clone, Copy, PartialEq)]
133pub struct VariantDecimal8 {
134    integer: i64,
135    scale: u8,
136}
137
138impl VariantDecimal8 {
139    pub(crate) const MAX_PRECISION: u8 = 18;
140    pub(crate) const MAX_UNSCALED_VALUE: u64 = u64::pow(10, Self::MAX_PRECISION as u32) - 1;
141
142    pub fn try_new(integer: i64, scale: u8) -> Result<Self, ArrowError> {
143        decimal_try_new!(integer, scale)
144    }
145
146    /// Returns the underlying value of the decimal.
147    ///
148    /// For example, if the decimal is `123456.78`, this will return `12345678`.
149    pub fn integer(&self) -> i64 {
150        self.integer
151    }
152
153    /// Returns the scale of the decimal (how many digits after the decimal point).
154    ///
155    /// For example, if the decimal is `123456.78`, this will return `2`.
156    pub fn scale(&self) -> u8 {
157        self.scale
158    }
159}
160
161impl fmt::Display for VariantDecimal8 {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        format_decimal!(f, self.integer, self.scale, i64)
164    }
165}
166
167/// Represents an 16-byte decimal value in the Variant format.
168///
169/// This struct stores a decimal number using a 128-bit signed integer for the coefficient
170/// and an 8-bit unsigned integer for the scale (number of decimal places). Its precision is between 19 and 38 digits.
171///
172/// For valid precision and scale values, see the Variant specification:
173///
174/// <https://github.com/apache/parquet-format/blob/87f2c8bf77eefb4c43d0ebaeea1778bd28ac3609/VariantEncoding.md?plain=1#L418-L420>
175///
176/// # Example: Create a VariantDecimal16
177/// ```
178/// # use parquet_variant::VariantDecimal16;
179/// // Create a value representing the decimal 12345678901234567.890
180/// let decimal = VariantDecimal16::try_new(12345678901234567890, 3).unwrap();
181/// ```
182#[derive(Debug, Clone, Copy, PartialEq)]
183pub struct VariantDecimal16 {
184    integer: i128,
185    scale: u8,
186}
187
188impl VariantDecimal16 {
189    const MAX_PRECISION: u8 = 38;
190    const MAX_UNSCALED_VALUE: u128 = u128::pow(10, Self::MAX_PRECISION as u32) - 1;
191
192    pub fn try_new(integer: i128, scale: u8) -> Result<Self, ArrowError> {
193        decimal_try_new!(integer, scale)
194    }
195
196    /// Returns the underlying value of the decimal.
197    ///
198    /// For example, if the decimal is `12345678901234567.890`, this will return `12345678901234567890`.
199    pub fn integer(&self) -> i128 {
200        self.integer
201    }
202
203    /// Returns the scale of the decimal (how many digits after the decimal point).
204    ///
205    /// For example, if the decimal is `12345678901234567.890`, this will return `3`.
206    pub fn scale(&self) -> u8 {
207        self.scale
208    }
209}
210
211impl fmt::Display for VariantDecimal16 {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        format_decimal!(f, self.integer, self.scale, i128)
214    }
215}
216
217// Infallible conversion from a narrower decimal type to a wider one
218macro_rules! impl_from_decimal_for_decimal {
219    ($from_ty:ty, $for_ty:ty) => {
220        impl From<$from_ty> for $for_ty {
221            fn from(decimal: $from_ty) -> Self {
222                Self {
223                    integer: decimal.integer.into(),
224                    scale: decimal.scale,
225                }
226            }
227        }
228    };
229}
230
231impl_from_decimal_for_decimal!(VariantDecimal4, VariantDecimal8);
232impl_from_decimal_for_decimal!(VariantDecimal4, VariantDecimal16);
233impl_from_decimal_for_decimal!(VariantDecimal8, VariantDecimal16);
234
235// Fallible conversion from a wider decimal type to a narrower one
236macro_rules! impl_try_from_decimal_for_decimal {
237    ($from_ty:ty, $for_ty:ty) => {
238        impl TryFrom<$from_ty> for $for_ty {
239            type Error = ArrowError;
240
241            fn try_from(decimal: $from_ty) -> Result<Self, ArrowError> {
242                let Ok(integer) = decimal.integer.try_into() else {
243                    return Err(ArrowError::InvalidArgumentError(format!(
244                        "Value {} is wider than max precision {}",
245                        decimal.integer,
246                        Self::MAX_PRECISION
247                    )));
248                };
249                Self::try_new(integer, decimal.scale)
250            }
251        }
252    };
253}
254
255impl_try_from_decimal_for_decimal!(VariantDecimal8, VariantDecimal4);
256impl_try_from_decimal_for_decimal!(VariantDecimal16, VariantDecimal4);
257impl_try_from_decimal_for_decimal!(VariantDecimal16, VariantDecimal8);
258
259// Fallible conversion from a decimal's underlying integer type
260macro_rules! impl_try_from_int_for_decimal {
261    ($from_ty:ty, $for_ty:ty) => {
262        impl TryFrom<$from_ty> for $for_ty {
263            type Error = ArrowError;
264
265            fn try_from(integer: $from_ty) -> Result<Self, ArrowError> {
266                Self::try_new(integer, 0)
267            }
268        }
269    };
270}
271
272impl_try_from_int_for_decimal!(i32, VariantDecimal4);
273impl_try_from_int_for_decimal!(i64, VariantDecimal8);
274impl_try_from_int_for_decimal!(i128, VariantDecimal16);
275
276#[cfg(test)]
277mod tests {
278    use super::*;
279
280    #[test]
281    fn test_variant_decimal_invalid_precision() {
282        // Test precision validation for Decimal4
283        let decimal4_too_large = VariantDecimal4::try_new(1_000_000_000_i32, 2);
284        assert!(
285            decimal4_too_large.is_err(),
286            "Decimal4 precision overflow should fail"
287        );
288        assert!(decimal4_too_large
289            .unwrap_err()
290            .to_string()
291            .contains("wider than max precision"));
292
293        let decimal4_too_small = VariantDecimal4::try_new(-1_000_000_000_i32, 2);
294        assert!(
295            decimal4_too_small.is_err(),
296            "Decimal4 precision underflow should fail"
297        );
298        assert!(decimal4_too_small
299            .unwrap_err()
300            .to_string()
301            .contains("wider than max precision"));
302
303        // Test valid edge cases for Decimal4
304        let decimal4_max_valid = VariantDecimal4::try_new(999_999_999_i32, 2);
305        assert!(
306            decimal4_max_valid.is_ok(),
307            "Decimal4 max valid value should succeed"
308        );
309
310        let decimal4_min_valid = VariantDecimal4::try_new(-999_999_999_i32, 2);
311        assert!(
312            decimal4_min_valid.is_ok(),
313            "Decimal4 min valid value should succeed"
314        );
315
316        // Test precision validation for Decimal8
317        let decimal8_too_large = VariantDecimal8::try_new(1_000_000_000_000_000_000_i64, 2);
318        assert!(
319            decimal8_too_large.is_err(),
320            "Decimal8 precision overflow should fail"
321        );
322        assert!(decimal8_too_large
323            .unwrap_err()
324            .to_string()
325            .contains("wider than max precision"));
326
327        let decimal8_too_small = VariantDecimal8::try_new(-1_000_000_000_000_000_000_i64, 2);
328        assert!(
329            decimal8_too_small.is_err(),
330            "Decimal8 precision underflow should fail"
331        );
332        assert!(decimal8_too_small
333            .unwrap_err()
334            .to_string()
335            .contains("wider than max precision"));
336
337        // Test valid edge cases for Decimal8
338        let decimal8_max_valid = VariantDecimal8::try_new(999_999_999_999_999_999_i64, 2);
339        assert!(
340            decimal8_max_valid.is_ok(),
341            "Decimal8 max valid value should succeed"
342        );
343
344        let decimal8_min_valid = VariantDecimal8::try_new(-999_999_999_999_999_999_i64, 2);
345        assert!(
346            decimal8_min_valid.is_ok(),
347            "Decimal8 min valid value should succeed"
348        );
349
350        // Test precision validation for Decimal16
351        let decimal16_too_large =
352            VariantDecimal16::try_new(100000000000000000000000000000000000000_i128, 2);
353        assert!(
354            decimal16_too_large.is_err(),
355            "Decimal16 precision overflow should fail"
356        );
357        assert!(decimal16_too_large
358            .unwrap_err()
359            .to_string()
360            .contains("wider than max precision"));
361
362        let decimal16_too_small =
363            VariantDecimal16::try_new(-100000000000000000000000000000000000000_i128, 2);
364        assert!(
365            decimal16_too_small.is_err(),
366            "Decimal16 precision underflow should fail"
367        );
368        assert!(decimal16_too_small
369            .unwrap_err()
370            .to_string()
371            .contains("wider than max precision"));
372
373        // Test valid edge cases for Decimal16
374        let decimal16_max_valid =
375            VariantDecimal16::try_new(99999999999999999999999999999999999999_i128, 2);
376        assert!(
377            decimal16_max_valid.is_ok(),
378            "Decimal16 max valid value should succeed"
379        );
380
381        let decimal16_min_valid =
382            VariantDecimal16::try_new(-99999999999999999999999999999999999999_i128, 2);
383        assert!(
384            decimal16_min_valid.is_ok(),
385            "Decimal16 min valid value should succeed"
386        );
387    }
388
389    #[test]
390    fn test_variant_decimal_invalid_scale() {
391        // Test invalid scale for Decimal4 (scale > 9)
392        let decimal4_invalid_scale = VariantDecimal4::try_new(123_i32, 10);
393        assert!(
394            decimal4_invalid_scale.is_err(),
395            "Decimal4 with scale > 9 should fail"
396        );
397        assert!(decimal4_invalid_scale
398            .unwrap_err()
399            .to_string()
400            .contains("larger than max precision"));
401
402        let decimal4_invalid_scale_large = VariantDecimal4::try_new(123_i32, 20);
403        assert!(
404            decimal4_invalid_scale_large.is_err(),
405            "Decimal4 with scale > 9 should fail"
406        );
407
408        // Test valid scale edge case for Decimal4
409        let decimal4_valid_scale = VariantDecimal4::try_new(123_i32, 9);
410        assert!(
411            decimal4_valid_scale.is_ok(),
412            "Decimal4 with scale = 9 should succeed"
413        );
414
415        // Test invalid scale for Decimal8 (scale > 18)
416        let decimal8_invalid_scale = VariantDecimal8::try_new(123_i64, 19);
417        assert!(
418            decimal8_invalid_scale.is_err(),
419            "Decimal8 with scale > 18 should fail"
420        );
421        assert!(decimal8_invalid_scale
422            .unwrap_err()
423            .to_string()
424            .contains("larger than max precision"));
425
426        let decimal8_invalid_scale_large = VariantDecimal8::try_new(123_i64, 25);
427        assert!(
428            decimal8_invalid_scale_large.is_err(),
429            "Decimal8 with scale > 18 should fail"
430        );
431
432        // Test valid scale edge case for Decimal8
433        let decimal8_valid_scale = VariantDecimal8::try_new(123_i64, 18);
434        assert!(
435            decimal8_valid_scale.is_ok(),
436            "Decimal8 with scale = 18 should succeed"
437        );
438
439        // Test invalid scale for Decimal16 (scale > 38)
440        let decimal16_invalid_scale = VariantDecimal16::try_new(123_i128, 39);
441        assert!(
442            decimal16_invalid_scale.is_err(),
443            "Decimal16 with scale > 38 should fail"
444        );
445        assert!(decimal16_invalid_scale
446            .unwrap_err()
447            .to_string()
448            .contains("larger than max precision"));
449
450        let decimal16_invalid_scale_large = VariantDecimal16::try_new(123_i128, 50);
451        assert!(
452            decimal16_invalid_scale_large.is_err(),
453            "Decimal16 with scale > 38 should fail"
454        );
455
456        // Test valid scale edge case for Decimal16
457        let decimal16_valid_scale = VariantDecimal16::try_new(123_i128, 38);
458        assert!(
459            decimal16_valid_scale.is_ok(),
460            "Decimal16 with scale = 38 should succeed"
461        );
462    }
463
464    #[test]
465    fn test_variant_decimal4_display() {
466        // Test zero scale (integers)
467        let d = VariantDecimal4::try_new(42, 0).unwrap();
468        assert_eq!(d.to_string(), "42");
469
470        let d = VariantDecimal4::try_new(-42, 0).unwrap();
471        assert_eq!(d.to_string(), "-42");
472
473        // Test basic decimal formatting
474        let d = VariantDecimal4::try_new(12345, 2).unwrap();
475        assert_eq!(d.to_string(), "123.45");
476
477        let d = VariantDecimal4::try_new(-12345, 2).unwrap();
478        assert_eq!(d.to_string(), "-123.45");
479
480        // Test trailing zeros are trimmed
481        let d = VariantDecimal4::try_new(12300, 2).unwrap();
482        assert_eq!(d.to_string(), "123");
483
484        let d = VariantDecimal4::try_new(-12300, 2).unwrap();
485        assert_eq!(d.to_string(), "-123");
486
487        // Test leading zeros in decimal part
488        let d = VariantDecimal4::try_new(1005, 3).unwrap();
489        assert_eq!(d.to_string(), "1.005");
490
491        let d = VariantDecimal4::try_new(-1005, 3).unwrap();
492        assert_eq!(d.to_string(), "-1.005");
493
494        // Test number smaller than scale (leading zero before decimal)
495        let d = VariantDecimal4::try_new(123, 4).unwrap();
496        assert_eq!(d.to_string(), "0.0123");
497
498        let d = VariantDecimal4::try_new(-123, 4).unwrap();
499        assert_eq!(d.to_string(), "-0.0123");
500
501        // Test zero
502        let d = VariantDecimal4::try_new(0, 0).unwrap();
503        assert_eq!(d.to_string(), "0");
504
505        let d = VariantDecimal4::try_new(0, 3).unwrap();
506        assert_eq!(d.to_string(), "0");
507
508        // Test max scale
509        let d = VariantDecimal4::try_new(123456789, 9).unwrap();
510        assert_eq!(d.to_string(), "0.123456789");
511
512        let d = VariantDecimal4::try_new(-123456789, 9).unwrap();
513        assert_eq!(d.to_string(), "-0.123456789");
514
515        // Test max precision
516        let d = VariantDecimal4::try_new(999999999, 0).unwrap();
517        assert_eq!(d.to_string(), "999999999");
518
519        let d = VariantDecimal4::try_new(-999999999, 0).unwrap();
520        assert_eq!(d.to_string(), "-999999999");
521
522        // Test trailing zeros with mixed decimal places
523        let d = VariantDecimal4::try_new(120050, 4).unwrap();
524        assert_eq!(d.to_string(), "12.005");
525
526        let d = VariantDecimal4::try_new(-120050, 4).unwrap();
527        assert_eq!(d.to_string(), "-12.005");
528    }
529
530    #[test]
531    fn test_variant_decimal8_display() {
532        // Test zero scale (integers)
533        let d = VariantDecimal8::try_new(42, 0).unwrap();
534        assert_eq!(d.to_string(), "42");
535
536        let d = VariantDecimal8::try_new(-42, 0).unwrap();
537        assert_eq!(d.to_string(), "-42");
538
539        // Test basic decimal formatting
540        let d = VariantDecimal8::try_new(1234567890, 3).unwrap();
541        assert_eq!(d.to_string(), "1234567.89");
542
543        let d = VariantDecimal8::try_new(-1234567890, 3).unwrap();
544        assert_eq!(d.to_string(), "-1234567.89");
545
546        // Test trailing zeros are trimmed
547        let d = VariantDecimal8::try_new(123000000, 6).unwrap();
548        assert_eq!(d.to_string(), "123");
549
550        let d = VariantDecimal8::try_new(-123000000, 6).unwrap();
551        assert_eq!(d.to_string(), "-123");
552
553        // Test leading zeros in decimal part
554        let d = VariantDecimal8::try_new(100005, 6).unwrap();
555        assert_eq!(d.to_string(), "0.100005");
556
557        let d = VariantDecimal8::try_new(-100005, 6).unwrap();
558        assert_eq!(d.to_string(), "-0.100005");
559
560        // Test number smaller than scale
561        let d = VariantDecimal8::try_new(123, 10).unwrap();
562        assert_eq!(d.to_string(), "0.0000000123");
563
564        let d = VariantDecimal8::try_new(-123, 10).unwrap();
565        assert_eq!(d.to_string(), "-0.0000000123");
566
567        // Test zero
568        let d = VariantDecimal8::try_new(0, 0).unwrap();
569        assert_eq!(d.to_string(), "0");
570
571        let d = VariantDecimal8::try_new(0, 10).unwrap();
572        assert_eq!(d.to_string(), "0");
573
574        // Test max scale
575        let d = VariantDecimal8::try_new(123456789012345678, 18).unwrap();
576        assert_eq!(d.to_string(), "0.123456789012345678");
577
578        let d = VariantDecimal8::try_new(-123456789012345678, 18).unwrap();
579        assert_eq!(d.to_string(), "-0.123456789012345678");
580
581        // Test max precision
582        let d = VariantDecimal8::try_new(999999999999999999, 0).unwrap();
583        assert_eq!(d.to_string(), "999999999999999999");
584
585        let d = VariantDecimal8::try_new(-999999999999999999, 0).unwrap();
586        assert_eq!(d.to_string(), "-999999999999999999");
587
588        // Test complex trailing zeros
589        let d = VariantDecimal8::try_new(1200000050000, 10).unwrap();
590        assert_eq!(d.to_string(), "120.000005");
591
592        let d = VariantDecimal8::try_new(-1200000050000, 10).unwrap();
593        assert_eq!(d.to_string(), "-120.000005");
594    }
595
596    #[test]
597    fn test_variant_decimal16_display() {
598        // Test zero scale (integers)
599        let d = VariantDecimal16::try_new(42, 0).unwrap();
600        assert_eq!(d.to_string(), "42");
601
602        let d = VariantDecimal16::try_new(-42, 0).unwrap();
603        assert_eq!(d.to_string(), "-42");
604
605        // Test basic decimal formatting
606        let d = VariantDecimal16::try_new(123456789012345, 4).unwrap();
607        assert_eq!(d.to_string(), "12345678901.2345");
608
609        let d = VariantDecimal16::try_new(-123456789012345, 4).unwrap();
610        assert_eq!(d.to_string(), "-12345678901.2345");
611
612        // Test trailing zeros are trimmed
613        let d = VariantDecimal16::try_new(12300000000, 8).unwrap();
614        assert_eq!(d.to_string(), "123");
615
616        let d = VariantDecimal16::try_new(-12300000000, 8).unwrap();
617        assert_eq!(d.to_string(), "-123");
618
619        // Test leading zeros in decimal part
620        let d = VariantDecimal16::try_new(10000005, 8).unwrap();
621        assert_eq!(d.to_string(), "0.10000005");
622
623        let d = VariantDecimal16::try_new(-10000005, 8).unwrap();
624        assert_eq!(d.to_string(), "-0.10000005");
625
626        // Test number smaller than scale
627        let d = VariantDecimal16::try_new(123, 20).unwrap();
628        assert_eq!(d.to_string(), "0.00000000000000000123");
629
630        let d = VariantDecimal16::try_new(-123, 20).unwrap();
631        assert_eq!(d.to_string(), "-0.00000000000000000123");
632
633        // Test zero
634        let d = VariantDecimal16::try_new(0, 0).unwrap();
635        assert_eq!(d.to_string(), "0");
636
637        let d = VariantDecimal16::try_new(0, 20).unwrap();
638        assert_eq!(d.to_string(), "0");
639
640        // Test max scale
641        let d = VariantDecimal16::try_new(12345678901234567890123456789012345678_i128, 38).unwrap();
642        assert_eq!(d.to_string(), "0.12345678901234567890123456789012345678");
643
644        let d =
645            VariantDecimal16::try_new(-12345678901234567890123456789012345678_i128, 38).unwrap();
646        assert_eq!(d.to_string(), "-0.12345678901234567890123456789012345678");
647
648        // Test max precision integer
649        let d = VariantDecimal16::try_new(99999999999999999999999999999999999999_i128, 0).unwrap();
650        assert_eq!(d.to_string(), "99999999999999999999999999999999999999");
651
652        let d = VariantDecimal16::try_new(-99999999999999999999999999999999999999_i128, 0).unwrap();
653        assert_eq!(d.to_string(), "-99999999999999999999999999999999999999");
654
655        // Test complex trailing zeros
656        let d = VariantDecimal16::try_new(12000000000000050000000000000_i128, 25).unwrap();
657        assert_eq!(d.to_string(), "1200.000000000005");
658
659        let d = VariantDecimal16::try_new(-12000000000000050000000000000_i128, 25).unwrap();
660        assert_eq!(d.to_string(), "-1200.000000000005");
661
662        // Test large integer that would overflow i64 but fits in i128
663        let large_int = 12345678901234567890123456789_i128;
664        let d = VariantDecimal16::try_new(large_int, 0).unwrap();
665        assert_eq!(d.to_string(), "12345678901234567890123456789");
666
667        let d = VariantDecimal16::try_new(-large_int, 0).unwrap();
668        assert_eq!(d.to_string(), "-12345678901234567890123456789");
669    }
670}