-
Notifications
You must be signed in to change notification settings - Fork 5.4k
/
Copy pathbigdecimal.c
7738 lines (6992 loc) · 199 KB
/
bigdecimal.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
*
* Ruby BigDecimal(Variable decimal precision) extension library.
*
* Copyright(C) 2002 by Shigeo Kobayashi(shigeo@tinyforest.gr.jp)
*
*/
/* #define BIGDECIMAL_DEBUG 1 */
#include "bigdecimal.h"
#include "ruby/util.h"
#ifndef BIGDECIMAL_DEBUG
# undef NDEBUG
# define NDEBUG
#endif
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
#include "bits.h"
#include "static_assert.h"
#define BIGDECIMAL_VERSION "3.1.5"
/* #define ENABLE_NUMERIC_STRING */
#define SIGNED_VALUE_MAX INTPTR_MAX
#define SIGNED_VALUE_MIN INTPTR_MIN
#define MUL_OVERFLOW_SIGNED_VALUE_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, SIGNED_VALUE_MIN, SIGNED_VALUE_MAX)
VALUE rb_cBigDecimal;
VALUE rb_mBigMath;
static ID id_BigDecimal_exception_mode;
static ID id_BigDecimal_rounding_mode;
static ID id_BigDecimal_precision_limit;
static ID id_up;
static ID id_down;
static ID id_truncate;
static ID id_half_up;
static ID id_default;
static ID id_half_down;
static ID id_half_even;
static ID id_banker;
static ID id_ceiling;
static ID id_ceil;
static ID id_floor;
static ID id_to_r;
static ID id_eq;
static ID id_half;
#define RBD_NUM_ROUNDING_MODES 11
static struct {
ID id;
uint8_t mode;
} rbd_rounding_modes[RBD_NUM_ROUNDING_MODES];
/* MACRO's to guard objects from GC by keeping them in stack */
#ifdef RBIMPL_ATTR_MAYBE_UNUSED
#define ENTER(n) RBIMPL_ATTR_MAYBE_UNUSED() volatile VALUE vStack[n];int iStack=0
#else
#define ENTER(n) volatile VALUE RB_UNUSED_VAR(vStack[n]);int iStack=0
#endif
#define PUSH(x) (vStack[iStack++] = (VALUE)(x))
#define SAVE(p) PUSH((p)->obj)
#define GUARD_OBJ(p,y) ((p)=(y), SAVE(p))
#define BASE_FIG BIGDECIMAL_COMPONENT_FIGURES
#define BASE BIGDECIMAL_BASE
#define HALF_BASE (BASE/2)
#define BASE1 (BASE/10)
#define LOG10_2 0.3010299956639812
#ifndef RRATIONAL_ZERO_P
# define RRATIONAL_ZERO_P(x) (FIXNUM_P(rb_rational_num(x)) && \
FIX2LONG(rb_rational_num(x)) == 0)
#endif
#ifndef RRATIONAL_NEGATIVE_P
# define RRATIONAL_NEGATIVE_P(x) RTEST(rb_funcall((x), '<', 1, INT2FIX(0)))
#endif
#ifndef DECIMAL_SIZE_OF_BITS
#define DECIMAL_SIZE_OF_BITS(n) (((n) * 3010 + 9998) / 9999)
/* an approximation of ceil(n * log10(2)), upto 65536 at least */
#endif
#ifdef PRIsVALUE
# define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj)
# define RB_OBJ_STRING(obj) (obj)
#else
# define PRIsVALUE "s"
# define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj)
# define RB_OBJ_STRING(obj) StringValueCStr(obj)
#endif
#ifndef MAYBE_UNUSED
# define MAYBE_UNUSED(x) x
#endif
#define BIGDECIMAL_POSITIVE_P(bd) ((bd)->sign > 0)
#define BIGDECIMAL_NEGATIVE_P(bd) ((bd)->sign < 0)
/*
* ================== Memory allocation ============================
*/
#ifdef BIGDECIMAL_DEBUG
static size_t rbd_allocation_count = 0; /* Memory allocation counter */
static inline void
atomic_allocation_count_inc(void)
{
RUBY_ATOMIC_SIZE_INC(rbd_allocation_count);
}
static inline void
atomic_allocation_count_dec_nounderflow(void)
{
if (rbd_allocation_count == 0) return;
RUBY_ATOMIC_SIZE_DEC(rbd_allocation_count);
}
static void
check_allocation_count_nonzero(void)
{
if (rbd_allocation_count != 0) return;
rb_bug("[bigdecimal][rbd_free_struct] Too many memory free calls");
}
#else
# define atomic_allocation_count_inc() /* nothing */
# define atomic_allocation_count_dec_nounderflow() /* nothing */
# define check_allocation_count_nonzero() /* nothing */
#endif /* BIGDECIMAL_DEBUG */
PUREFUNC(static inline size_t rbd_struct_size(size_t const));
static inline size_t
rbd_struct_size(size_t const internal_digits)
{
size_t const frac_len = (internal_digits == 0) ? 1 : internal_digits;
return offsetof(Real, frac) + frac_len * sizeof(DECDIG);
}
static inline Real *
rbd_allocate_struct(size_t const internal_digits)
{
size_t const size = rbd_struct_size(internal_digits);
Real *real = ruby_xcalloc(1, size);
atomic_allocation_count_inc();
real->MaxPrec = internal_digits;
return real;
}
static size_t
rbd_calculate_internal_digits(size_t const digits, bool limit_precision)
{
size_t const len = roomof(digits, BASE_FIG);
if (limit_precision) {
size_t const prec_limit = VpGetPrecLimit();
if (prec_limit > 0) {
/* NOTE: 2 more digits for rounding and division */
size_t const max_len = roomof(prec_limit, BASE_FIG) + 2;
if (len > max_len)
return max_len;
}
}
return len;
}
static inline Real *
rbd_allocate_struct_decimal_digits(size_t const decimal_digits, bool limit_precision)
{
size_t const internal_digits = rbd_calculate_internal_digits(decimal_digits, limit_precision);
return rbd_allocate_struct(internal_digits);
}
static VALUE BigDecimal_wrap_struct(VALUE obj, Real *vp);
static Real *
rbd_reallocate_struct(Real *real, size_t const internal_digits)
{
size_t const size = rbd_struct_size(internal_digits);
VALUE obj = real ? real->obj : 0;
Real *new_real = (Real *)ruby_xrealloc(real, size);
new_real->MaxPrec = internal_digits;
if (obj) {
new_real->obj = 0;
BigDecimal_wrap_struct(obj, new_real);
}
return new_real;
}
static void
rbd_free_struct(Real *real)
{
if (real != NULL) {
check_allocation_count_nonzero();
ruby_xfree(real);
atomic_allocation_count_dec_nounderflow();
}
}
#define NewZero rbd_allocate_struct_zero
static Real *
rbd_allocate_struct_zero(int sign, size_t const digits, bool limit_precision)
{
Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision);
VpSetZero(real, sign);
return real;
}
MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited(int sign, size_t const digits));
#define NewZeroLimited rbd_allocate_struct_zero_limited
static inline Real *
rbd_allocate_struct_zero_limited(int sign, size_t const digits)
{
return rbd_allocate_struct_zero(sign, digits, true);
}
MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit(int sign, size_t const digits));
#define NewZeroNolimit rbd_allocate_struct_zero_nolimit
static inline Real *
rbd_allocate_struct_zero_nolimit(int sign, size_t const digits)
{
return rbd_allocate_struct_zero(sign, digits, false);
}
#define NewOne rbd_allocate_struct_one
static Real *
rbd_allocate_struct_one(int sign, size_t const digits, bool limit_precision)
{
Real *real = rbd_allocate_struct_decimal_digits(digits, limit_precision);
VpSetOne(real);
if (sign < 0)
VpSetSign(real, VP_SIGN_NEGATIVE_FINITE);
return real;
}
MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited(int sign, size_t const digits));
#define NewOneLimited rbd_allocate_struct_one_limited
static inline Real *
rbd_allocate_struct_one_limited(int sign, size_t const digits)
{
return rbd_allocate_struct_one(sign, digits, true);
}
MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit(int sign, size_t const digits));
#define NewOneNolimit rbd_allocate_struct_one_nolimit
static inline Real *
rbd_allocate_struct_one_nolimit(int sign, size_t const digits)
{
return rbd_allocate_struct_one(sign, digits, false);
}
/*
* ================== Ruby Interface part ==========================
*/
#define DoSomeOne(x,y,f) rb_num_coerce_bin(x,y,f)
/*
* VP routines used in BigDecimal part
*/
static unsigned short VpGetException(void);
static void VpSetException(unsigned short f);
static void VpCheckException(Real *p, bool always);
static VALUE VpCheckGetValue(Real *p);
static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
static int VpLimitRound(Real *c, size_t ixDigit);
static Real *VpCopy(Real *pv, Real const* const x);
static int VPrint(FILE *fp,const char *cntl_chr,Real *a);
/*
* **** BigDecimal part ****
*/
static VALUE BigDecimal_nan(void);
static VALUE BigDecimal_positive_infinity(void);
static VALUE BigDecimal_negative_infinity(void);
static VALUE BigDecimal_positive_zero(void);
static VALUE BigDecimal_negative_zero(void);
static void
BigDecimal_delete(void *pv)
{
rbd_free_struct(pv);
}
static size_t
BigDecimal_memsize(const void *ptr)
{
const Real *pv = ptr;
return (sizeof(*pv) + pv->MaxPrec * sizeof(DECDIG));
}
#ifndef HAVE_RB_EXT_RACTOR_SAFE
# undef RUBY_TYPED_FROZEN_SHAREABLE
# define RUBY_TYPED_FROZEN_SHAREABLE 0
#endif
static const rb_data_type_t BigDecimal_data_type = {
"BigDecimal",
{ 0, BigDecimal_delete, BigDecimal_memsize, },
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED
#endif
};
static Real *
rbd_allocate_struct_zero_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision)
{
Real *real = rbd_allocate_struct_zero(sign, digits, limit_precision);
if (real != NULL) {
VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
BigDecimal_wrap_struct(obj, real);
}
return real;
}
MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits));
#define NewZeroWrapLimited rbd_allocate_struct_zero_limited_wrap
static inline Real *
rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits)
{
return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, true);
}
MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits));
#define NewZeroWrapNolimit rbd_allocate_struct_zero_nolimit_wrap
static inline Real *
rbd_allocate_struct_zero_nolimit_wrap(int sign, size_t const digits)
{
return rbd_allocate_struct_zero_wrap_klass(rb_cBigDecimal, sign, digits, false);
}
static Real *
rbd_allocate_struct_one_wrap_klass(VALUE klass, int sign, size_t const digits, bool limit_precision)
{
Real *real = rbd_allocate_struct_one(sign, digits, limit_precision);
if (real != NULL) {
VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, 0);
BigDecimal_wrap_struct(obj, real);
}
return real;
}
MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits));
#define NewOneWrapLimited rbd_allocate_struct_one_limited_wrap
static inline Real *
rbd_allocate_struct_one_limited_wrap(int sign, size_t const digits)
{
return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, true);
}
MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits));
#define NewOneWrapNolimit rbd_allocate_struct_one_nolimit_wrap
static inline Real *
rbd_allocate_struct_one_nolimit_wrap(int sign, size_t const digits)
{
return rbd_allocate_struct_one_wrap_klass(rb_cBigDecimal, sign, digits, false);
}
static inline int
is_kind_of_BigDecimal(VALUE const v)
{
return rb_typeddata_is_kind_of(v, &BigDecimal_data_type);
}
NORETURN(static void cannot_be_coerced_into_BigDecimal(VALUE, VALUE));
static void
cannot_be_coerced_into_BigDecimal(VALUE exc_class, VALUE v)
{
VALUE str;
if (rb_special_const_p(v)) {
str = rb_inspect(v);
}
else {
str = rb_class_name(rb_obj_class(v));
}
str = rb_str_cat2(rb_str_dup(str), " can't be coerced into BigDecimal");
rb_exc_raise(rb_exc_new3(exc_class, str));
}
static inline VALUE BigDecimal_div2(VALUE, VALUE, VALUE);
static VALUE rb_inum_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
static VALUE rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
static VALUE rb_rational_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
static VALUE rb_cstr_convert_to_BigDecimal(const char *c_str, size_t digs, int raise_exception);
static VALUE rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception);
static Real*
GetVpValueWithPrec(VALUE v, long prec, int must)
{
const size_t digs = prec < 0 ? SIZE_MAX : (size_t)prec;
switch(TYPE(v)) {
case T_FLOAT:
v = rb_float_convert_to_BigDecimal(v, digs, must);
break;
case T_RATIONAL:
v = rb_rational_convert_to_BigDecimal(v, digs, must);
break;
case T_DATA:
if (!is_kind_of_BigDecimal(v)) {
goto SomeOneMayDoIt;
}
break;
case T_FIXNUM: {
char szD[128];
snprintf(szD, 128, "%ld", FIX2LONG(v));
v = rb_cstr_convert_to_BigDecimal(szD, VpBaseFig() * 2 + 1, must);
break;
}
#ifdef ENABLE_NUMERIC_STRING
case T_STRING: {
const char *c_str = StringValueCStr(v);
v = rb_cstr_convert_to_BigDecimal(c_str, RSTRING_LEN(v) + VpBaseFig() + 1, must);
break;
}
#endif /* ENABLE_NUMERIC_STRING */
case T_BIGNUM: {
VALUE bg = rb_big2str(v, 10);
v = rb_cstr_convert_to_BigDecimal(RSTRING_PTR(bg), RSTRING_LEN(bg) + VpBaseFig() + 1, must);
RB_GC_GUARD(bg);
break;
}
default:
goto SomeOneMayDoIt;
}
Real *vp;
TypedData_Get_Struct(v, Real, &BigDecimal_data_type, vp);
return vp;
SomeOneMayDoIt:
if (must) {
cannot_be_coerced_into_BigDecimal(rb_eTypeError, v);
}
return NULL; /* NULL means to coerce */
}
static inline Real*
GetVpValue(VALUE v, int must)
{
return GetVpValueWithPrec(v, -1, must);
}
/* call-seq:
* BigDecimal.double_fig -> integer
*
* Returns the number of digits a Float object is allowed to have;
* the result is system-dependent:
*
* BigDecimal.double_fig # => 16
*
*/
static inline VALUE
BigDecimal_double_fig(VALUE self)
{
return INT2FIX(VpDblFig());
}
/* call-seq:
* precs -> array
*
* Returns an Array of two Integer values that represent platform-dependent
* internal storage properties.
*
* This method is deprecated and will be removed in the future.
* Instead, use BigDecimal#n_significant_digits for obtaining the number of
* significant digits in scientific notation, and BigDecimal#precision for
* obtaining the number of digits in decimal notation.
*
*/
static VALUE
BigDecimal_prec(VALUE self)
{
ENTER(1);
Real *p;
VALUE obj;
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
"BigDecimal#precs is deprecated and will be removed in the future; "
"use BigDecimal#precision instead.");
GUARD_OBJ(p, GetVpValue(self, 1));
obj = rb_assoc_new(SIZET2NUM(p->Prec*VpBaseFig()),
SIZET2NUM(p->MaxPrec*VpBaseFig()));
return obj;
}
static void
BigDecimal_count_precision_and_scale(VALUE self, ssize_t *out_precision, ssize_t *out_scale)
{
ENTER(1);
if (out_precision == NULL && out_scale == NULL)
return;
Real *p;
GUARD_OBJ(p, GetVpValue(self, 1));
if (VpIsZero(p) || !VpIsDef(p)) {
zero:
if (out_precision) *out_precision = 0;
if (out_scale) *out_scale = 0;
return;
}
DECDIG x;
ssize_t n = p->Prec; /* The length of frac without zeros. */
while (n > 0 && p->frac[n-1] == 0) --n;
if (n == 0) goto zero;
int nlz = BASE_FIG;
for (x = p->frac[0]; x > 0; x /= 10) --nlz;
int ntz = 0;
for (x = p->frac[n-1]; x > 0 && x % 10 == 0; x /= 10) ++ntz;
/*
* Calculate the precision and the scale
* -------------------------------------
*
* The most significant digit is frac[0], and the least significant digit
* is frac[Prec-1]. When the exponent is zero, the decimal point is
* located just before frac[0].
*
* When the exponent is negative, the decimal point moves to leftward.
* In this case, the precision can be calculated by
*
* precision = BASE_FIG * (-exponent + n) - ntz,
*
* and the scale is the same as precision.
*
* 0 . 0000 0000 | frac[0] ... frac[n-1] |
* |<----------| exponent == -2 |
* |---------------------------------->| precision
* |---------------------------------->| scale
*
*
* Conversely, when the exponent is positive, the decimal point moves to
* rightward. In this case, the scale equals to
*
* BASE_FIG * (n - exponent) - ntz.
*
* the precision equals to
*
* scale + BASE_FIG * exponent - nlz.
*
* | frac[0] frac[1] . frac[2] ... frac[n-1] |
* |---------------->| exponent == 2 |
* | |---------------------->| scale
* |---------------------------------------->| precision
*/
ssize_t ex = p->exponent;
/* Count the number of decimal digits before frac[1]. */
ssize_t n_digits_head = BASE_FIG;
if (ex < 0) {
n_digits_head += (-ex) * BASE_FIG; /* The number of leading zeros before frac[0]. */
ex = 0;
}
else if (ex > 0) {
/* Count the number of decimal digits without the leading zeros in
* the most significant digit in the integral part.
*/
n_digits_head -= nlz; /* Make the number of digits */
}
if (out_precision) {
ssize_t precision = n_digits_head;
/* Count the number of decimal digits after frac[0]. */
if (ex > (ssize_t)n) {
/* In this case the number is an integer with some trailing zeros. */
precision += (ex - 1) * BASE_FIG;
}
else if (n > 0) {
precision += (n - 1) * BASE_FIG;
if (ex < (ssize_t)n) {
precision -= ntz;
}
}
*out_precision = precision;
}
if (out_scale) {
ssize_t scale = 0;
if (p->exponent < 0) {
scale = n_digits_head + (n - 1) * BASE_FIG - ntz;
}
else if (n > p->exponent) {
scale = (n - p->exponent) * BASE_FIG - ntz;
}
*out_scale = scale;
}
}
/*
* call-seq:
* precision -> integer
*
* Returns the number of decimal digits in +self+:
*
* BigDecimal("0").precision # => 0
* BigDecimal("1").precision # => 1
* BigDecimal("1.1").precision # => 2
* BigDecimal("3.1415").precision # => 5
* BigDecimal("-1e20").precision # => 21
* BigDecimal("1e-20").precision # => 20
* BigDecimal("Infinity").precision # => 0
* BigDecimal("-Infinity").precision # => 0
* BigDecimal("NaN").precision # => 0
*
*/
static VALUE
BigDecimal_precision(VALUE self)
{
ssize_t precision;
BigDecimal_count_precision_and_scale(self, &precision, NULL);
return SSIZET2NUM(precision);
}
/*
* call-seq:
* scale -> integer
*
* Returns the number of decimal digits following the decimal digits in +self+.
*
* BigDecimal("0").scale # => 0
* BigDecimal("1").scale # => 0
* BigDecimal("1.1").scale # => 1
* BigDecimal("3.1415").scale # => 4
* BigDecimal("-1e20").precision # => 0
* BigDecimal("1e-20").precision # => 20
* BigDecimal("Infinity").scale # => 0
* BigDecimal("-Infinity").scale # => 0
* BigDecimal("NaN").scale # => 0
*/
static VALUE
BigDecimal_scale(VALUE self)
{
ssize_t scale;
BigDecimal_count_precision_and_scale(self, NULL, &scale);
return SSIZET2NUM(scale);
}
/*
* call-seq:
* precision_scale -> [integer, integer]
*
* Returns a 2-length array; the first item is the result of
* BigDecimal#precision and the second one is of BigDecimal#scale.
*
* See BigDecimal#precision.
* See BigDecimal#scale.
*/
static VALUE
BigDecimal_precision_scale(VALUE self)
{
ssize_t precision, scale;
BigDecimal_count_precision_and_scale(self, &precision, &scale);
return rb_assoc_new(SSIZET2NUM(precision), SSIZET2NUM(scale));
}
/*
* call-seq:
* n_significant_digits -> integer
*
* Returns the number of decimal significant digits in +self+.
*
* BigDecimal("0").n_significant_digits # => 0
* BigDecimal("1").n_significant_digits # => 1
* BigDecimal("1.1").n_significant_digits # => 2
* BigDecimal("3.1415").n_significant_digits # => 5
* BigDecimal("-1e20").n_significant_digits # => 1
* BigDecimal("1e-20").n_significant_digits # => 1
* BigDecimal("Infinity").n_significant_digits # => 0
* BigDecimal("-Infinity").n_significant_digits # => 0
* BigDecimal("NaN").n_significant_digits # => 0
*/
static VALUE
BigDecimal_n_significant_digits(VALUE self)
{
ENTER(1);
Real *p;
GUARD_OBJ(p, GetVpValue(self, 1));
if (VpIsZero(p) || !VpIsDef(p)) {
return INT2FIX(0);
}
ssize_t n = p->Prec; /* The length of frac without trailing zeros. */
for (n = p->Prec; n > 0 && p->frac[n-1] == 0; --n);
if (n == 0) return INT2FIX(0);
DECDIG x;
int nlz = BASE_FIG;
for (x = p->frac[0]; x > 0; x /= 10) --nlz;
int ntz = 0;
for (x = p->frac[n-1]; x > 0 && x % 10 == 0; x /= 10) ++ntz;
ssize_t n_significant_digits = BASE_FIG*n - nlz - ntz;
return SSIZET2NUM(n_significant_digits);
}
/*
* call-seq:
* hash -> integer
*
* Returns the integer hash value for +self+.
*
* Two instances of \BigDecimal have the same hash value if and only if
* they have equal:
*
* - Sign.
* - Fractional part.
* - Exponent.
*
*/
static VALUE
BigDecimal_hash(VALUE self)
{
ENTER(1);
Real *p;
st_index_t hash;
GUARD_OBJ(p, GetVpValue(self, 1));
hash = (st_index_t)p->sign;
/* hash!=2: the case for 0(1),NaN(0) or +-Infinity(3) is sign itself */
if(hash == 2 || hash == (st_index_t)-2) {
hash ^= rb_memhash(p->frac, sizeof(DECDIG)*p->Prec);
hash += p->exponent;
}
return ST2FIX(hash);
}
/*
* call-seq:
* _dump -> string
*
* Returns a string representing the marshalling of +self+.
* See module Marshal.
*
* inf = BigDecimal('Infinity') # => Infinity
* dumped = inf._dump # => "9:Infinity"
* BigDecimal._load(dumped) # => Infinity
*
*/
static VALUE
BigDecimal_dump(int argc, VALUE *argv, VALUE self)
{
ENTER(5);
Real *vp;
char *psz;
VALUE dummy;
volatile VALUE dump;
size_t len;
rb_scan_args(argc, argv, "01", &dummy);
GUARD_OBJ(vp,GetVpValue(self, 1));
dump = rb_str_new(0, VpNumOfChars(vp, "E")+50);
psz = RSTRING_PTR(dump);
snprintf(psz, RSTRING_LEN(dump), "%"PRIuSIZE":", VpMaxPrec(vp)*VpBaseFig());
len = strlen(psz);
VpToString(vp, psz+len, RSTRING_LEN(dump)-len, 0, 0);
rb_str_resize(dump, strlen(psz));
return dump;
}
/*
* Internal method used to provide marshalling support. See the Marshal module.
*/
static VALUE
BigDecimal_load(VALUE self, VALUE str)
{
ENTER(2);
Real *pv;
unsigned char *pch;
unsigned char ch;
unsigned long m=0;
pch = (unsigned char *)StringValueCStr(str);
/* First get max prec */
while((*pch) != (unsigned char)'\0' && (ch = *pch++) != (unsigned char)':') {
if(!ISDIGIT(ch)) {
rb_raise(rb_eTypeError, "load failed: invalid character in the marshaled string");
}
m = m*10 + (unsigned long)(ch-'0');
}
if (m > VpBaseFig()) m -= VpBaseFig();
GUARD_OBJ(pv, VpNewRbClass(m, (char *)pch, self, true, true));
m /= VpBaseFig();
if (m && pv->MaxPrec > m) {
pv->MaxPrec = m+1;
}
return VpCheckGetValue(pv);
}
static unsigned short
check_rounding_mode_option(VALUE const opts)
{
VALUE mode;
char const *s;
long l;
assert(RB_TYPE_P(opts, T_HASH));
if (NIL_P(opts))
goto no_opt;
mode = rb_hash_lookup2(opts, ID2SYM(id_half), Qundef);
if (mode == Qundef || NIL_P(mode))
goto no_opt;
if (SYMBOL_P(mode))
mode = rb_sym2str(mode);
else if (!RB_TYPE_P(mode, T_STRING)) {
VALUE str_mode = rb_check_string_type(mode);
if (NIL_P(str_mode))
goto invalid;
mode = str_mode;
}
s = RSTRING_PTR(mode);
l = RSTRING_LEN(mode);
switch (l) {
case 2:
if (strncasecmp(s, "up", 2) == 0)
return VP_ROUND_HALF_UP;
break;
case 4:
if (strncasecmp(s, "even", 4) == 0)
return VP_ROUND_HALF_EVEN;
else if (strncasecmp(s, "down", 4) == 0)
return VP_ROUND_HALF_DOWN;
break;
default:
break;
}
invalid:
rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", mode);
no_opt:
return VpGetRoundMode();
}
static unsigned short
check_rounding_mode(VALUE const v)
{
unsigned short sw;
ID id;
if (RB_TYPE_P(v, T_SYMBOL)) {
int i;
id = SYM2ID(v);
for (i = 0; i < RBD_NUM_ROUNDING_MODES; ++i) {
if (rbd_rounding_modes[i].id == id) {
return rbd_rounding_modes[i].mode;
}
}
rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v);
}
else {
sw = NUM2USHORT(v);
if (!VpIsRoundMode(sw)) {
rb_raise(rb_eArgError, "invalid rounding mode (%"PRIsVALUE")", v);
}
return sw;
}
}
/* call-seq:
* BigDecimal.mode(mode, setting = nil) -> integer
*
* Returns an integer representing the mode settings
* for exception handling and rounding.
*
* These modes control exception handling:
*
* - \BigDecimal::EXCEPTION_NaN.
* - \BigDecimal::EXCEPTION_INFINITY.
* - \BigDecimal::EXCEPTION_UNDERFLOW.
* - \BigDecimal::EXCEPTION_OVERFLOW.
* - \BigDecimal::EXCEPTION_ZERODIVIDE.
* - \BigDecimal::EXCEPTION_ALL.
*
* Values for +setting+ for exception handling:
*
* - +true+: sets the given +mode+ to +true+.
* - +false+: sets the given +mode+ to +false+.
* - +nil+: does not modify the mode settings.
*
* You can use method BigDecimal.save_exception_mode
* to temporarily change, and then automatically restore, exception modes.
*
* For clarity, some examples below begin by setting all
* exception modes to +false+.
*
* This mode controls the way rounding is to be performed:
*
* - \BigDecimal::ROUND_MODE
*
* You can use method BigDecimal.save_rounding_mode
* to temporarily change, and then automatically restore, the rounding mode.
*
* <b>NaNs</b>
*
* Mode \BigDecimal::EXCEPTION_NaN controls behavior
* when a \BigDecimal NaN is created.
*
* Settings:
*
* - +false+ (default): Returns <tt>BigDecimal('NaN')</tt>.
* - +true+: Raises FloatDomainError.
*
* Examples:
*
* BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
* BigDecimal('NaN') # => NaN
* BigDecimal.mode(BigDecimal::EXCEPTION_NaN, true) # => 2
* BigDecimal('NaN') # Raises FloatDomainError
*
* <b>Infinities</b>
*
* Mode \BigDecimal::EXCEPTION_INFINITY controls behavior
* when a \BigDecimal Infinity or -Infinity is created.
* Settings:
*
* - +false+ (default): Returns <tt>BigDecimal('Infinity')</tt>
* or <tt>BigDecimal('-Infinity')</tt>.
* - +true+: Raises FloatDomainError.
*
* Examples:
*
* BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
* BigDecimal('Infinity') # => Infinity
* BigDecimal('-Infinity') # => -Infinity
* BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, true) # => 1
* BigDecimal('Infinity') # Raises FloatDomainError
* BigDecimal('-Infinity') # Raises FloatDomainError
*
* <b>Underflow</b>
*
* Mode \BigDecimal::EXCEPTION_UNDERFLOW controls behavior
* when a \BigDecimal underflow occurs.
* Settings:
*
* - +false+ (default): Returns <tt>BigDecimal('0')</tt>
* or <tt>BigDecimal('-Infinity')</tt>.
* - +true+: Raises FloatDomainError.
*
* Examples:
*
* BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false) # => 0
* def flow_under
* x = BigDecimal('0.1')
* 100.times { x *= x }
* end
* flow_under # => 100
* BigDecimal.mode(BigDecimal::EXCEPTION_UNDERFLOW, true) # => 4
* flow_under # Raises FloatDomainError
*
* <b>Overflow</b>
*
* Mode \BigDecimal::EXCEPTION_OVERFLOW controls behavior
* when a \BigDecimal overflow occurs.
* Settings:
*
* - +false+ (default): Returns <tt>BigDecimal('Infinity')</tt>
* or <tt>BigDecimal('-Infinity')</tt>.