File Coverage

decimal.c
Criterion Covered Total %
statement 108 112 96.4
branch 92 132 69.7
condition n/a
subroutine n/a
pod n/a
total 200 244 81.9


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5              
6             #include
7             #include
8              
9             #if defined(__SIZEOF_INT128__) && __SIZEOF_INT128__ == 16
10             #define HAVE_INT128 1
11             typedef unsigned __int128 u128_t;
12             #endif
13              
14             #include "decimal.h"
15              
16             const uint64_t pow10_u64[20] = {
17             1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL,
18             1000000ULL, 10000000ULL, 100000000ULL, 1000000000ULL,
19             10000000000ULL, 100000000000ULL, 1000000000000ULL,
20             10000000000000ULL, 100000000000000ULL, 1000000000000000ULL,
21             10000000000000000ULL, 100000000000000000ULL,
22             1000000000000000000ULL, 10000000000000000000ULL
23             };
24              
25             static const double decimal_pow10_table[] = {
26             1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
27             1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
28             1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
29             1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38
30             };
31              
32 21           double decimal_pow10(int n) {
33 21 50         if (n >= 0 && n <= 38) return decimal_pow10_table[n];
    50          
34 0           return pow(10.0, n);
35             }
36              
37             /* Long-double powers of 10 for Decimal128/256 float-path multiplication.
38             * Computed at extended precision so the scale doesn't double-round. */
39 6           long double decimal_pow10l(int n) {
40 6 50         if (n < 0) return powl(10.0L, n);
41 6           long double r = 1.0L;
42             int i;
43 10 100         for (i = 0; i < n; i++) r *= 10.0L;
44 6           return r;
45             }
46              
47 7653           static int mul10_128(uint64_t *hi, uint64_t *lo) {
48             #ifdef HAVE_INT128
49 7653           u128_t prod_lo = (u128_t)*lo * 10;
50 7653           u128_t prod_hi = (u128_t)*hi * 10 + (uint64_t)(prod_lo >> 64);
51 7653           *lo = (uint64_t)prod_lo;
52 7653           *hi = (uint64_t)prod_hi;
53 7653           return (prod_hi >> 64) == 0;
54             #else
55             /* 32-bit limb fallback. */
56             uint64_t lo_lo = *lo & 0xFFFFFFFFULL;
57             uint64_t lo_hi = *lo >> 32;
58             uint64_t p_lo = lo_lo * 10;
59             uint64_t p_hi = lo_hi * 10 + (p_lo >> 32);
60             uint64_t carry = p_hi >> 32;
61             *lo = (p_hi << 32) | (p_lo & 0xFFFFFFFFULL);
62              
63             uint64_t hi_lo = *hi & 0xFFFFFFFFULL;
64             uint64_t hi_hi = *hi >> 32;
65             uint64_t q_lo = hi_lo * 10 + (carry & 0xFFFFFFFFULL);
66             uint64_t q_hi = hi_hi * 10 + (q_lo >> 32) + (carry >> 32);
67             *hi = (q_hi << 32) | (q_lo & 0xFFFFFFFFULL);
68             return (q_hi >> 32) == 0;
69             #endif
70             }
71              
72 571           static int mul10_256(uint64_t limbs[4]) {
73 571           uint64_t carry = 0;
74             int i;
75 2855 100         for (i = 0; i < 4; i++) {
76             #ifdef HAVE_INT128
77 2284           u128_t prod = (u128_t)limbs[i] * 10 + carry;
78 2284           limbs[i] = (uint64_t)prod;
79 2284           carry = (uint64_t)(prod >> 64);
80             #else
81             uint64_t lo_lo = limbs[i] & 0xFFFFFFFFULL;
82             uint64_t lo_hi = limbs[i] >> 32;
83             uint64_t p_lo = lo_lo * 10 + (carry & 0xFFFFFFFFULL);
84             uint64_t p_hi = lo_hi * 10 + (p_lo >> 32) + (carry >> 32);
85             limbs[i] = (p_hi << 32) | (p_lo & 0xFFFFFFFFULL);
86             carry = p_hi >> 32;
87             #endif
88             }
89 571           return carry == 0;
90             }
91              
92 575           int add_digit_256(uint64_t limbs[4], uint64_t digit) {
93 575           uint64_t carry = digit;
94             int i;
95 1110 100         for (i = 0; i < 4 && carry; i++) {
    100          
96 535           uint64_t old = limbs[i];
97 535           limbs[i] = old + carry;
98 535           carry = (limbs[i] < old) ? 1 : 0;
99             }
100 575           return (int)carry;
101             }
102              
103 2033           int parse_decimal128_str(const char *s, STRLEN len, int scale,
104             uint64_t *hi_out, uint64_t *lo_out) {
105 2033           STRLEN i = 0;
106 2033           int neg = 0, seen_digit = 0, seen_dot = 0, frac_used = 0;
107 2033           uint64_t lo = 0, hi = 0;
108              
109 2033 50         while (i < len && (s[i] == ' ' || s[i] == '\t')) i++;
    50          
    50          
110 2033 50         if (i < len && (s[i] == '+' || s[i] == '-')) {
    50          
    100          
111 10 50         if (s[i] == '-') neg = 1;
112 10           i++;
113             }
114              
115 8700 100         while (i < len) {
116 6668           char c = s[i++];
117 6668 100         if (c == '.') {
118 1016 50         if (seen_dot) return 0;
119 1016           seen_dot = 1;
120 1016           continue;
121             }
122 5652 50         if (c < '0' || c > '9') return 0;
    50          
123 5652           seen_digit = 1;
124 5652 100         if (seen_dot) {
125 2076 50         if (frac_used >= scale) continue; /* truncate */
126 2076           frac_used++;
127             }
128 5652 50         if (!mul10_128(&hi, &lo)) return 0;
129 5652           uint64_t digit = (uint64_t)(c - '0');
130             /* (hi:lo) + digit overflows iff hi == UINT64_MAX and lo wraps. */
131 5652 100         if (hi == 0xFFFFFFFFFFFFFFFFULL && lo > 0xFFFFFFFFFFFFFFFFULL - digit)
    100          
132 1           return 0;
133 5651           uint64_t newlo = lo + digit;
134 5651 100         if (newlo < lo) hi++;
135 5651           lo = newlo;
136             }
137              
138 2032 50         if (!seen_digit) return 0;
139              
140 4033 100         while (frac_used < scale) {
141 2001 50         if (!mul10_128(&hi, &lo)) return 0;
142 2001           frac_used++;
143             }
144              
145             /* Reject magnitudes that don't fit signed 128-bit (Decimal128 storage).
146             * The asymmetric extreme -2^127 has hi=0x80..., lo=0 with neg=1; allow it. */
147 2032 100         if (hi > 0x8000000000000000ULL) return 0;
148 2031 100         if (hi == 0x8000000000000000ULL && (!neg || lo != 0)) return 0;
    100          
    50          
149              
150 2029 100         if (neg) {
151 10           lo = ~lo + 1;
152 10 100         hi = ~hi + (lo == 0 ? 1 : 0);
153             }
154              
155 2029           *lo_out = lo;
156 2029           *hi_out = hi;
157 2029           return 1;
158             }
159              
160 13           int parse_decimal256_str(const char *s, STRLEN len, int scale,
161             uint64_t limbs_out[4]) {
162 13           STRLEN i = 0;
163 13           int neg = 0, seen_digit = 0, seen_dot = 0, frac_used = 0;
164 13           uint64_t limbs[4] = {0,0,0,0};
165              
166 13 50         while (i < len && (s[i] == ' ' || s[i] == '\t')) i++;
    50          
    50          
167 13 50         if (i < len && (s[i] == '+' || s[i] == '-')) {
    50          
    100          
168 4 50         if (s[i] == '-') neg = 1;
169 4           i++;
170             }
171 589 100         while (i < len) {
172 577           char c = s[i++];
173 577 100         if (c == '.') {
174 6 50         if (seen_dot) return 0;
175 6           seen_dot = 1;
176 6           continue;
177             }
178 571 50         if (c < '0' || c > '9') return 0;
    50          
179 571           seen_digit = 1;
180 571 100         if (seen_dot) {
181 16 50         if (frac_used >= scale) continue;
182 16           frac_used++;
183             }
184 571 50         if (!mul10_256(limbs)) return 0;
185 571 100         if (add_digit_256(limbs, (uint64_t)(c - '0'))) return 0;
186             }
187 12 50         if (!seen_digit) return 0;
188 12 50         while (frac_used < scale) {
189 0 0         if (!mul10_256(limbs)) return 0;
190 0           frac_used++;
191             }
192             /* Reject magnitudes that don't fit signed 256-bit. Allow only the
193             * asymmetric extreme -2^255 (top bit set, rest zero, negative). */
194 12 50         if (limbs[3] > 0x8000000000000000ULL) return 0;
195 12 100         if (limbs[3] == 0x8000000000000000ULL
196 2 100         && (!neg || limbs[0] != 0 || limbs[1] != 0 || limbs[2] != 0))
    50          
    50          
    50          
197 1           return 0;
198 11 100         if (neg) {
199             int j;
200 20 100         for (j = 0; j < 4; j++) limbs[j] = ~limbs[j];
201 4           add_digit_256(limbs, 1);
202             }
203 11           limbs_out[0] = limbs[0];
204 11           limbs_out[1] = limbs[1];
205 11           limbs_out[2] = limbs[2];
206 11           limbs_out[3] = limbs[3];
207 11           return 1;
208             }
209              
210 2013           int parse_decimal_int64_str(const char *s, STRLEN len, int scale,
211             int64_t *out) {
212             uint64_t lo, hi;
213 2013 50         if (!parse_decimal128_str(s, len, scale, &hi, &lo)) return 0;
214             /* Fits in int64 iff hi is sign extension of lo's high bit. */
215 2013 100         if (hi == 0 && (lo & 0x8000000000000000ULL) == 0) {
    50          
216 2010           *out = (int64_t)lo;
217 2010           return 1;
218             }
219 3 50         if (hi == ~(uint64_t)0 && (lo & 0x8000000000000000ULL)) {
    50          
220 3           *out = (int64_t)lo;
221 3           return 1;
222             }
223 0           return 0;
224             }