File Coverage

Ryu.xs
Criterion Covered Total %
statement 205 226 90.7
branch 122 136 89.7
condition n/a
subroutine n/a
pod n/a
total 327 362 90.3


line stmt bran cond sub pod time code
1             #ifdef __MINGW32__
2             #ifndef __USE_MINGW_ANSI_STDIO
3             #define __USE_MINGW_ANSI_STDIO 1
4             #endif
5             #endif
6              
7             #define PERL_NO_GET_CONTEXT 1
8              
9             #include "EXTERN.h"
10             #include "perl.h"
11             #include "XSUB.h"
12              
13             #include
14             #include
15              
16             #if defined(COMPILER_HAS_UINT128_T) && !defined(AVOID_GENERIC_128)
17             # include /* modified to include stdbool.h */
18             # include
19             # include
20             # ifdef USE_QUADMATH
21             # include /* do we actually need this ? */
22             # endif
23             #endif
24              
25             #define QUAD_MANTISSA_BITS 112
26             #define QUAD_EXPONENT_BITS 15
27              
28             #include "math_ryu_include.h"
29              
30             typedef struct floating_decimal_128 t_fd128;
31              
32             #if defined(COMPILER_HAS_UINT128_T) && !defined(AVOID_GENERIC_128) && MAX_DEC_DIG == 36
33             /* To be called only by q2s(). Cannot be accessed directly from perl */
34             struct floating_decimal_128 quad_to_fd128(NV d) {
35             uint128_t bits = 0;
36             memcpy(&bits, &d, 16);
37             return generic_binary_to_decimal(bits, QUAD_MANTISSA_BITS, QUAD_EXPONENT_BITS, false);
38             }
39             #endif
40              
41 9460           double _s2d(char * buffer) {
42             #if NVSIZE == 8
43             double nv;
44 9460           s2d(buffer, &nv);
45 9460           return nv;
46             #else
47             PERL_UNUSED_ARG(buffer);
48             croak("s2d() is available only to perls whose NV is of type 'double'");
49             #endif
50             }
51              
52 7626           SV * M_RYU_d2s(pTHX_ SV * nv) {
53             #if NVSIZE == 8
54 7626           return newSVpv(d2s(SvNV(nv)), 0);
55             #else
56             PERL_UNUSED_ARG(nv);
57             croak("d2s() is available only to perls whose NV is of type 'double'");
58             #endif
59             }
60              
61 0           SV * ld2s(pTHX_ SV * nv) {
62             #if MAX_DEC_DIG == 21
63             char * buff;
64             SV * outsv;
65              
66             Newxz(buff, LD_BUF, char); /* LD_BUF defined in math_ryu_l)include.h, along with D_BUF and F128_BUF */
67              
68             if(buff == NULL) croak("Failed to allocate memory for string buffer in ld2s sub");
69             generic_to_chars(long_double_to_fd128(SvNV(nv)), buff);
70             outsv = newSVpv(buff, 0);
71             Safefree(buff);
72             return outsv;
73             #else
74             PERL_UNUSED_ARG(nv);
75 0           croak("ld2s() is available only to perls whose NV is of type 80-bit extended precision 'long double'");
76             #endif
77             }
78              
79 0           SV * q2s(pTHX_ SV * nv) {
80             #if MAX_DEC_DIG == 36
81             char * buff;
82             SV * outsv;
83              
84             Newxz(buff, F128_BUF, char);
85              
86             if(buff == NULL) croak("Failed to allocate memory for string buffer in ld2s sub");
87             generic_to_chars(quad_to_fd128(SvNV(nv)), buff);
88             outsv = newSVpv(buff, 0);
89             Safefree(buff);
90             return outsv;
91             #else
92             PERL_UNUSED_ARG(nv);
93 0           croak("q2s() is available only to perls whose NV is either '__float128' or IEEE 754 'long double'");
94             #endif
95             }
96              
97 2           SV * fx2s(pTHX_ SV * nv) {
98 2           return newSVpv(f2s((float)SvNV(nv)), 0);
99             }
100              
101 20           int ryu_SvIOK(SV * sv) {
102 20 100         if(SvIOK(sv)) return 1;
103 18           return 0;
104             }
105              
106 7           int ryu_SvNOK(SV * sv) {
107 7 100         if(SvNOK(sv)) return 1;
108 3           return 0;
109             }
110              
111 33           int ryu_SvPOK(SV * sv) {
112 33 100         if(SvPOK(sv)) return 1;
113 16           return 0;
114             }
115              
116 0           int ryu_SvIOKp(SV * sv) {
117 0 0         if(SvIOKp(sv)) return 1;
118 0           return 0;
119             }
120              
121 9706           int ryu_lln(pTHX_ SV * sv) {
122 9706           return looks_like_number(sv);
123             }
124              
125 14           int ryu_refcnt(SV * sv) {
126 14           return SvREFCNT(sv);
127             }
128              
129 1           int _compiler_has_uint128(void) {
130             #ifdef COMPILER_HAS_UINT128_T
131 1           return 1;
132             #else
133             return 0;
134             #endif
135             }
136              
137 1           int _get_max_dec_dig(void) {
138             #ifdef MAX_DEC_DIG
139 1           return MAX_DEC_DIG;
140             #else
141             return 0;
142             #endif
143             }
144              
145 2791           SV * fmtpy(pTHX_ SV * in) {
146 2791           int is_neg = 0, pointpos = 0, exponent, dec = 0, zero_pad = 0;
147             char *man_str, *exp_str;
148 2791           size_t i, len, seen = 0;
149 2791           char *s = SvPV_nolen(in);
150             SV * outsv;
151              
152 2791 100         if(s[0] == '-') {
153 558           is_neg = 1;
154 558           s++;
155             }
156              
157             /* If the given argument contains a decimal point, then that *
158             * decimal point will immediately follow the first digit. *
159             * Otherwise, the argument does not contain a decimal point. */
160 2791 100         if(s[1] == '.') pointpos = 1;
161              
162 2791 100         if(pointpos) {
163             /*** STRING CONTAINS A DECIMAL POINT ***/
164 2564           Newxz(man_str, MAN_BUF, char);
165 2564 50         if(man_str == NULL) croak("Failed (in 'if' block) to allocate memory for man_str in fmtpy function");
166 2564           Newxz(exp_str, EXP_BUF, char);
167 2564 50         if(exp_str == NULL) croak("Failed (in 'if' block) to allocate memory for exp_str in fmtpy function");
168              
169             /* Split into mantissa and exponent around 'E' *
170             * The input string (s) has the 'E' replaced by a NULL *
171             * and the exponent portion of s is copied to exp_str */
172 2564           len = strlen(s);
173 44070 100         for(i = 0; i < len; i++) {
174 41506 100         if(seen) { /* will be set when 'E' is encountered' */
175 7790           exp_str[i - seen] = s[i];
176             }
177 33716 100         else if(s[i] == 'E') {
178 2564           seen = i + 1;
179 2564           s[i] = '\0';
180             }
181             }
182 2564           exp_str[i - seen] = '\0';
183 2564           exponent = atoi(exp_str);
184              
185 2564 100         if(exponent > 0 && exponent < MAX_DEC_DIG) {
    100          
186 165           len = strlen(s);
187 165           zero_pad = exponent - ((int)len - 2);
188 165 100         if(zero_pad >= 0 && (zero_pad + len < MAX_DEC_DIG + 1)) {
    100          
189             /* Return, eg, '1.23E15' as '1230000000000000.0' */
190 64 100         if(is_neg) man_str[0] = '-';
191 64           man_str[0 + is_neg] = s[0];
192 688 100         for(i = 2; i < len; i++) {
193 624           man_str[i + is_neg - 1] = s[i];
194             }
195 229 100         for(dec = 0; dec < zero_pad; dec++) {
196 165           man_str[i + is_neg - 1] = '0';
197 165           i++;
198             }
199 64           man_str[i + is_neg - 1] = '.';
200 64           man_str[i + is_neg] = '0';
201 64           man_str[i + is_neg + 1] = '\0';
202              
203 64           outsv = newSVpv(man_str, 0);
204 64           Safefree(man_str);
205 64           Safefree(exp_str);
206 64 100         if(is_neg) s--;
207 64           return outsv;
208             }
209 101 100         else if(zero_pad < 0) {
210             /* We want, eg, 1.23625E2 to be returned as "123.625". *
211             * This involves relocation of the decimal point. */
212 85           len = strlen(s);
213 85           zero_pad--;
214 85 100         if(is_neg) man_str[0] = '-';
215 85           man_str[0 + is_neg] = s[0];
216 85           man_str[1 + is_neg] = s[2]; /* s[1] is the decimal point */
217 85           dec = 1; /* set to 0 when the decimal point is encountered */
218 953 100         for(i = 2; i < len; i++) {
219 868 100         if(i == zero_pad + len) {
220 85           man_str[i + is_neg] = '.';
221 85           dec--;
222             }
223 783           else man_str[i + is_neg] = s[i + dec];
224             }
225              
226 85           man_str[i + is_neg] = '\0';
227              
228 85           outsv = newSVpv(man_str, 0);
229 85           Safefree(man_str);
230 85           Safefree(exp_str);
231 85 100         if(is_neg) s--;
232 85           return outsv;
233             }
234             }
235              
236 2415           len = strlen(s); /* now different to when initially set *
237             * because 'E' has been replaced with '\0' */
238 2415 100         if(exponent < -4 || exponent >= 0) {
    100          
239 2359 100         if(is_neg) man_str[0] = '-';
240 30971 100         for(i = 0; i < len; i++) man_str[i + is_neg] = s[i];
241 2359 100         if(!exponent) {
242 15           man_str[i + is_neg] = '\0';
243 15           outsv = newSVpv(man_str, 0);
244 15           Safefree(man_str);
245 15           Safefree(exp_str);
246 15 100         if(is_neg) s--;
247 15           return outsv;
248             }
249 2344           man_str[i + is_neg] = 'e';
250 2344           len = strlen(exp_str);
251 2344 100         if(exponent > 0) {
252 1056           man_str[i + 1 + is_neg] = '+';
253 1056           dec = (int)i + 2 + is_neg;
254 3868 100         for(i = 0; i < len; i ++) man_str[dec + i] = exp_str[i];
255 1056           man_str[dec + i] = '\0';
256 1056           outsv = newSVpv(man_str, 0);
257 1056           Safefree(man_str);
258 1056           Safefree(exp_str);
259 1056 100         if(is_neg) s--;
260 1056           return outsv;
261             }
262             /* exponent < -4 */
263 1288           dec = (int)i + 1 + is_neg;
264 1288 100         if(len == 2) {
265 53           man_str[dec] = '-';
266 53           man_str[dec + 1] = '0';
267 53           man_str[dec + 2] = exp_str[1];
268 53           man_str[dec + 3] = '\0';
269             }
270             else {
271 5768 100         for(i = 0; i < len; i++) man_str[dec + i] = exp_str[i];
272 1235           man_str[dec + i] = '\0';
273             }
274 1288           outsv = newSVpv(man_str, 0);
275 1288           Safefree(man_str);
276 1288           Safefree(exp_str);
277 1288 100         if(is_neg) s--;
278 1288           return outsv;
279             }
280             /* exponent is in range -1 to -4 (inclusive). *
281             * Return, eg 6.25E1 as "0.625". **************/
282 56 100         if(is_neg) man_str[0] = '-';
283 56           man_str[is_neg] = '0';
284 56           man_str[1 + is_neg] = '.';
285 56           zero_pad = -exponent;
286 56           zero_pad --;
287 109 100         for(i = 0; i < (size_t)zero_pad; i++) man_str[i + 2 + is_neg] = '0';
288 56           man_str[i + 2 + is_neg] = s[0];
289 56           dec = (int)i + 1 + is_neg;
290 56           len = strlen(s);
291 694 100         for(i = 2; i < len; i++) man_str[dec + i] = s[i];
292 56           man_str[dec + i] = '\0';
293 56           outsv = newSVpv(man_str, 0);
294 56           Safefree(man_str);
295 56           Safefree(exp_str);
296 56 100         if(is_neg) s--;
297 56           return outsv;
298             }
299             else {
300             /*** NO DECIMAL POINT IN STRING ***/
301 227 100         if(s[0] == 'I') {
302 193 100         if(is_neg) {
303 38           s--;
304             /* if(SvIV(get_sv("Math::Ryu::PERL_INFNAN", 0))) return newSVpv(SvPV_nolen(get_sv("Math::Ryu::ninfstr", 0)), 0); */
305 38 100         if(SvIV(get_sv("Math::Ryu::PERL_INFNAN", 0))) {
306 1           outsv = SvREFCNT_inc(get_sv("Math::Ryu::ninfstr", 0));
307 1           return outsv;
308             }
309 37           return newSVpv("-inf", 0);
310             }
311             else {
312             /* if(SvIV(get_sv("Math::Ryu::PERL_INFNAN", 0))) return newSVpv(SvPV_nolen(get_sv("Math::Ryu::pinfstr", 0)), 0); */
313 155 100         if(SvIV(get_sv("Math::Ryu::PERL_INFNAN", 0))) {
314 1           outsv = SvREFCNT_inc(get_sv("Math::Ryu::pinfstr", 0));
315 1           return outsv;
316             }
317 154           return newSVpv("inf", 0);
318             }
319             }
320 34 100         if(s[0] == 'N') {
321 3 50         if(is_neg) {
322 0           s--;
323 0           croak("rubbish input of '%s'", s);
324             }
325             /* if(SvIV(get_sv("Math::Ryu::PERL_INFNAN", 0))) return newSVpv(SvPV_nolen(get_sv("Math::Ryu::nanvstr", 0)), 0); */
326 3 100         if(SvIV(get_sv("Math::Ryu::PERL_INFNAN", 0))) {
327 2           outsv = SvREFCNT_inc(get_sv("Math::Ryu::nanvstr", 0));
328 2           return outsv;
329             }
330 1           return newSVpv("nan", 0);
331             }
332             /* mantissa is single-digit */
333 31           len = strlen(s);
334 31           Newxz(exp_str, MAX_DEC_DIG + 6, char);
335 31 50         if(exp_str == NULL) croak("Failed (in 'else' block) to allocate memory for exp_str in fmtpy function");
336 152 100         for(i = 2; i <= len; i++) exp_str[i - 2] = s[i];
337 31           exponent = atoi(exp_str);
338 31 100         if(exponent < -9) {
339             /* Return, eg, '5E-10' as '5e-10' */
340 17           s[1] = 'e';
341 17 100         if(is_neg) s--;
342 17           Safefree(exp_str);
343 17           return newSVpv(s, 0);
344             }
345             /* Put the string to return into exp_str */
346 14 100         if(exponent < -4) {
347             /* Return, eg, '5E-9' as '5e-09'. */
348 2 100         if(is_neg) exp_str[0] = '-';
349 2           exp_str[0 + is_neg] = s[0];
350 2           exp_str[1 + is_neg] = 'e';
351 2           exp_str[2 + is_neg] = '-';
352 2           exp_str[3 + is_neg] = '0';
353 2           exp_str[4 + is_neg] = s[3];
354 2           exp_str[5 + is_neg] = '\0';
355 2           outsv = newSVpv(exp_str, 0);
356 2           Safefree(exp_str);
357 2 100         if(is_neg) s--;
358 2           return outsv;
359             }
360              
361 12 50         if(exponent >= 0 && exponent <= MAX_DEC_DIG - 2) {
    100          
362             /* Return, eg, '5E13' as '50000000000000.0' */
363 8 100         if(is_neg) exp_str[0] = '-';
364 8           exp_str[0 + is_neg] = s[0];
365 56 100         for(dec = 0; dec < exponent; dec++) exp_str[1 + is_neg + dec] = '0';
366 8           exp_str[1 + is_neg + dec] = '.';
367 8           exp_str[2 + is_neg + dec] = '0';
368 8           exp_str[3 + is_neg + dec] = '\0';
369              
370 8           outsv = newSVpv(exp_str, 0);
371 8           Safefree(exp_str);
372 8 100         if(is_neg) s--;
373 8           return outsv;
374             }
375              
376 4 50         if(exponent > MAX_DEC_DIG - 2) {
377             /* Return, eg, '8E50' as '8e+50' */
378 4 100         if(is_neg) exp_str[0] = '-';
379 4           exp_str[0 + is_neg] = s[0];
380 4           exp_str[1 + is_neg] = 'e';
381 4           exp_str[2 + is_neg] = '+';
382 12 100         for(i = 2; i < len; i++) exp_str[i + 1 + is_neg] = s[i];
383 4           exp_str[i + 1 + is_neg] = '\0';
384 4           outsv = newSVpv(exp_str, 0);
385 4           Safefree(exp_str);
386 4 100         if(is_neg) s--;
387 4           return outsv;
388             }
389              
390             /* Exponent is in the range -1 to -4. We *
391             * want, eg, '7E-1' to be returned as '0.7' *
392             * and '-9E-4' to be returned as '-0.0009' */
393              
394 0 0         if(is_neg) exp_str[0] = '-';
395 0           exp_str[0 + is_neg] = '0';
396 0           exp_str[1 + is_neg] = '.';
397 0 0         for(dec = -1; dec > exponent; dec--) exp_str[1 + is_neg - dec] = '0';
398 0           exp_str[1 + is_neg - dec] = s[0];
399 0           exp_str[2 + is_neg - dec] = '\0';
400              
401 0           outsv = newSVpv(exp_str, 0);
402 0           Safefree(exp_str);
403 0 0         if(is_neg) s--;
404 0           return outsv;
405             }
406             }
407              
408 3           SV * _from_NV(pTHX_ NV arg ) {
409 3 100         if(arg >= 0) {
410 1           return newSVuv((UV)arg);
411             }
412 2           return newSViv((IV)arg);
413             }
414              
415              
416             MODULE = Math::Ryu PACKAGE = Math::Ryu PREFIX = M_RYU_
417              
418             PROTOTYPES: DISABLE
419              
420             double
421             _s2d (buffer)
422             char * buffer
423              
424             SV *
425             M_RYU_d2s (nv)
426             SV * nv
427             CODE:
428 7626           RETVAL = M_RYU_d2s (aTHX_ nv);
429             OUTPUT: RETVAL
430              
431             SV *
432             ld2s (nv)
433             SV * nv
434             CODE:
435 0           RETVAL = ld2s (aTHX_ nv);
436             OUTPUT: RETVAL
437              
438             SV *
439             q2s (nv)
440             SV * nv
441             CODE:
442 0           RETVAL = q2s (aTHX_ nv);
443             OUTPUT: RETVAL
444              
445             SV *
446             fx2s (nv)
447             SV * nv
448             CODE:
449 2           RETVAL = fx2s (aTHX_ nv);
450             OUTPUT: RETVAL
451              
452             int
453             ryu_SvIOK (sv)
454             SV * sv
455              
456             int
457             ryu_SvNOK (sv)
458             SV * sv
459              
460             int
461             ryu_SvPOK (sv)
462             SV * sv
463              
464             int
465             ryu_SvIOKp (sv)
466             SV * sv
467              
468             int
469             ryu_lln (sv)
470             SV * sv
471             CODE:
472 9706           RETVAL = ryu_lln (aTHX_ sv);
473             OUTPUT: RETVAL
474              
475             int
476             ryu_refcnt (sv)
477             SV * sv
478              
479             int
480             _compiler_has_uint128 ()
481              
482             int
483             _get_max_dec_dig ()
484              
485             SV *
486             fmtpy (in)
487             SV * in
488             CODE:
489 2791           RETVAL = fmtpy (aTHX_ in);
490             OUTPUT: RETVAL
491              
492             SV *
493             _from_NV (arg)
494             NV arg
495             CODE:
496 3           RETVAL = _from_NV (aTHX_ arg);
497             OUTPUT: RETVAL
498              
499