This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Document string- and number-specific bitops in perlop
[perl5.git] / dist / Math-BigInt / t / mbimbf.inc
CommitLineData
c4a6f826 1# test rounding, accuracy, precision and fallback, round_mode and mixing
61f5c3f5
T
2# of classes
3
4# Make sure you always quote any bare floating-point values, lest 123.46 will
5# be stringified to 123.4599999999 due to limited float prevision.
6
56d9de68 7use strict;
61f5c3f5
T
8my ($x,$y,$z,$u,$rc);
9
10###############################################################################
11# test defaults and set/get
12
56d9de68
T
13{
14 no strict 'refs';
3167abe5
NC
15 is (${"$mbi\::accuracy"}, undef);
16 is (${"$mbi\::precision"}, undef);
17 is ($mbi->accuracy(), undef);
18 is ($mbi->precision(), undef);
19 is (${"$mbi\::div_scale"},40);
20 is (${"$mbi\::round_mode"},'even');
21 is ($mbi->round_mode(),'even');
22
23 is (${"$mbf\::accuracy"}, undef);
24 is (${"$mbf\::precision"}, undef);
25 is ($mbf->precision(), undef);
26 is ($mbf->precision(), undef);
27 is (${"$mbf\::div_scale"},40);
28 is (${"$mbf\::round_mode"},'even');
29 is ($mbf->round_mode(),'even');
56d9de68 30}
61f5c3f5
T
31
32# accessors
33foreach my $class ($mbi,$mbf)
34 {
3167abe5
NC
35 is ($class->accuracy(), undef);
36 is ($class->precision(), undef);
37 is ($class->round_mode(),'even');
38 is ($class->div_scale(),40);
61f5c3f5 39
3167abe5
NC
40 is ($class->div_scale(20),20);
41 $class->div_scale(40); is ($class->div_scale(),40);
61f5c3f5 42
3167abe5
NC
43 is ($class->round_mode('odd'),'odd');
44 $class->round_mode('even'); is ($class->round_mode(),'even');
61f5c3f5 45
3167abe5
NC
46 is ($class->accuracy(2),2);
47 $class->accuracy(3); is ($class->accuracy(),3);
48 is ($class->accuracy(undef), undef);
49
50 is ($class->precision(2),2);
51 is ($class->precision(-2),-2);
52 $class->precision(3); is ($class->precision(),3);
53 is ($class->precision(undef), undef);
61f5c3f5
T
54 }
55
56d9de68
T
56{
57 no strict 'refs';
58 # accuracy
59 foreach (qw/5 42 -1 0/)
60 {
3167abe5
NC
61 is (${"$mbf\::accuracy"} = $_,$_);
62 is (${"$mbi\::accuracy"} = $_,$_);
56d9de68 63 }
3167abe5
NC
64 is (${"$mbf\::accuracy"} = undef, undef);
65 is (${"$mbi\::accuracy"} = undef, undef);
61f5c3f5 66
56d9de68
T
67 # precision
68 foreach (qw/5 42 -1 0/)
69 {
3167abe5
NC
70 is (${"$mbf\::precision"} = $_,$_);
71 is (${"$mbi\::precision"} = $_,$_);
56d9de68 72 }
3167abe5
NC
73 is (${"$mbf\::precision"} = undef, undef);
74 is (${"$mbi\::precision"} = undef, undef);
61f5c3f5 75
56d9de68
T
76 # fallback
77 foreach (qw/5 42 1/)
78 {
3167abe5
NC
79 is (${"$mbf\::div_scale"} = $_,$_);
80 is (${"$mbi\::div_scale"} = $_,$_);
56d9de68
T
81 }
82 # illegal values are possible for fallback due to no accessor
61f5c3f5 83
56d9de68
T
84 # round_mode
85 foreach (qw/odd even zero trunc +inf -inf/)
86 {
3167abe5
NC
87 is (${"$mbf\::round_mode"} = $_,$_);
88 is (${"$mbi\::round_mode"} = $_,$_);
56d9de68
T
89 }
90 ${"$mbf\::round_mode"} = 'zero';
3167abe5
NC
91 is (${"$mbf\::round_mode"},'zero');
92 is (${"$mbi\::round_mode"},'-inf'); # from above
56d9de68 93
2ab5f49d 94 # reset for further tests
56d9de68
T
95 ${"$mbi\::accuracy"} = undef;
96 ${"$mbi\::precision"} = undef;
2ab5f49d 97 ${"$mbf\::div_scale"} = 40;
56d9de68 98}
61f5c3f5 99
61f5c3f5
T
100# local copies
101$x = $mbf->new('123.456');
3167abe5
NC
102is ($x->accuracy(), undef);
103is ($x->accuracy(5),5);
104is ($x->accuracy(undef),undef, undef);
105is ($x->precision(), undef);
106is ($x->precision(5),5);
107is ($x->precision(undef),undef, undef);
61f5c3f5 108
56d9de68
T
109{
110 no strict 'refs';
111 # see if MBF changes MBIs values
3167abe5
NC
112 is (${"$mbi\::accuracy"} = 42,42);
113 is (${"$mbf\::accuracy"} = 64,64);
114 is (${"$mbi\::accuracy"},42); # should be still 42
115 is (${"$mbf\::accuracy"},64); # should be now 64
56d9de68 116}
61f5c3f5
T
117
118###############################################################################
119# see if creating a number under set A or P will round it
120
56d9de68
T
121{
122 no strict 'refs';
123 ${"$mbi\::accuracy"} = 4;
124 ${"$mbi\::precision"} = undef;
61f5c3f5 125
3167abe5 126 is ($mbi->new(123456),123500); # with A
56d9de68
T
127 ${"$mbi\::accuracy"} = undef;
128 ${"$mbi\::precision"} = 3;
3167abe5 129 is ($mbi->new(123456),123000); # with P
61f5c3f5 130
56d9de68
T
131 ${"$mbf\::accuracy"} = 4;
132 ${"$mbf\::precision"} = undef;
133 ${"$mbi\::precision"} = undef;
61f5c3f5 134
3167abe5 135 is ($mbf->new('123.456'),'123.5'); # with A
56d9de68
T
136 ${"$mbf\::accuracy"} = undef;
137 ${"$mbf\::precision"} = -1;
3167abe5 138 is ($mbf->new('123.456'),'123.5'); # with P from MBF, not MBI!
61f5c3f5 139
56d9de68
T
140 ${"$mbf\::precision"} = undef; # reset
141}
61f5c3f5
T
142
143###############################################################################
144# see if MBI leaves MBF's private parts alone
145
56d9de68
T
146{
147 no strict 'refs';
148 ${"$mbi\::precision"} = undef; ${"$mbf\::precision"} = undef;
149 ${"$mbi\::accuracy"} = 4; ${"$mbf\::accuracy"} = undef;
3167abe5 150 is ($mbf->new('123.456'),'123.456');
56d9de68
T
151 ${"$mbi\::accuracy"} = undef; # reset
152}
61f5c3f5
T
153
154###############################################################################
155# see if setting accuracy/precision actually rounds the number
156
3167abe5
NC
157$x = $mbf->new('123.456'); $x->accuracy(4); is ($x,'123.5');
158$x = $mbf->new('123.456'); $x->precision(-2); is ($x,'123.46');
61f5c3f5 159
3167abe5
NC
160$x = $mbi->new(123456); $x->accuracy(4); is ($x,123500);
161$x = $mbi->new(123456); $x->precision(2); is ($x,123500);
61f5c3f5
T
162
163###############################################################################
164# test actual rounding via round()
165
166$x = $mbf->new('123.456');
3167abe5
NC
167is ($x->copy()->round(5),'123.46');
168is ($x->copy()->round(4),'123.5');
169is ($x->copy()->round(5,2),'NaN');
170is ($x->copy()->round(undef,-2),'123.46');
171is ($x->copy()->round(undef,2),120);
61f5c3f5
T
172
173$x = $mbi->new('123');
3167abe5 174is ($x->round(5,2),'NaN');
61f5c3f5
T
175
176$x = $mbf->new('123.45000');
3167abe5 177is ($x->copy()->round(undef,-1,'odd'),'123.5');
61f5c3f5
T
178
179# see if rounding is 'sticky'
180$x = $mbf->new('123.4567');
181$y = $x->copy()->bround(); # no-op since nowhere A or P defined
182
3167abe5 183is ($y,123.4567);
61f5c3f5 184$y = $x->copy()->round(5);
3167abe5
NC
185is ($y->accuracy(),5);
186is ($y->precision(), undef); # A has precedence, so P still unset
61f5c3f5 187$y = $x->copy()->round(undef,2);
3167abe5
NC
188is ($y->precision(),2);
189is ($y->accuracy(), undef); # P has precedence, so A still unset
61f5c3f5
T
190
191# see if setting A clears P and vice versa
192$x = $mbf->new('123.4567');
3167abe5
NC
193is ($x,'123.4567');
194is ($x->accuracy(4),4);
195is ($x->precision(-2),-2); # clear A
196is ($x->accuracy(), undef);
61f5c3f5
T
197
198$x = $mbf->new('123.4567');
3167abe5
NC
199is ($x,'123.4567');
200is ($x->precision(-2),-2);
201is ($x->accuracy(4),4); # clear P
202is ($x->precision(), undef);
61f5c3f5
T
203
204# does copy work?
205$x = $mbf->new(123.456); $x->accuracy(4); $x->precision(2);
3167abe5 206$z = $x->copy(); is ($z->accuracy(),undef); is ($z->precision(),2);
61f5c3f5 207
56d9de68
T
208# does $x->bdiv($y,d) work when $d > div_scale?
209$x = $mbf->new('0.008'); $x->accuracy(8);
210
211for my $e ( 4, 8, 16, 32 )
212 {
213 print "# Tried: $x->bdiv(3,$e)\n"
3167abe5 214 unless is (scalar $x->copy()->bdiv(3,$e), '0.002' . ('6' x ($e-2)) . '7');
56d9de68
T
215 }
216
61f5c3f5 217# does accuracy()/precision work on zeros?
56d9de68 218foreach my $c ($mbi,$mbf)
61f5c3f5 219 {
3167abe5
NC
220 $x = $c->bzero(); $x->accuracy(5); is ($x->{_a},5);
221 $x = $c->bzero(); $x->precision(5); is ($x->{_p},5);
222 $x = $c->new(0); $x->accuracy(5); is ($x->{_a},5);
223 $x = $c->new(0); $x->precision(5); is ($x->{_p},5);
61f5c3f5 224
3167abe5
NC
225 $x = $c->bzero(); $x->round(5); is ($x->{_a},5);
226 $x = $c->bzero(); $x->round(undef,5); is ($x->{_p},5);
227 $x = $c->new(0); $x->round(5); is ($x->{_a},5);
228 $x = $c->new(0); $x->round(undef,5); is ($x->{_p},5);
61f5c3f5
T
229
230 # see if trying to increasing A in bzero() doesn't do something
3167abe5 231 $x = $c->bzero(); $x->{_a} = 3; $x->round(5); is ($x->{_a},3);
56d9de68
T
232 }
233
234###############################################################################
235# test whether an opp calls objectify properly or not (or at least does what
236# it should do given non-objects, w/ or w/o objectify())
237
238foreach my $c ($mbi,$mbf)
239 {
240# ${"$c\::precision"} = undef; # reset
241# ${"$c\::accuracy"} = undef; # reset
242
3167abe5
NC
243 is ($c->new(123)->badd(123),246);
244 is ($c->badd(123,321),444);
245 is ($c->badd(123,$c->new(321)),444);
56d9de68 246
3167abe5
NC
247 is ($c->new(123)->bsub(122),1);
248 is ($c->bsub(321,123),198);
249 is ($c->bsub(321,$c->new(123)),198);
56d9de68 250
3167abe5
NC
251 is ($c->new(123)->bmul(123),15129);
252 is ($c->bmul(123,123),15129);
253 is ($c->bmul(123,$c->new(123)),15129);
56d9de68 254
3167abe5
NC
255# is ($c->new(15129)->bdiv(123),123);
256# is ($c->bdiv(15129,123),123);
257# is ($c->bdiv(15129,$c->new(123)),123);
56d9de68 258
3167abe5
NC
259 is ($c->new(15131)->bmod(123),2);
260 is ($c->bmod(15131,123),2);
261 is ($c->bmod(15131,$c->new(123)),2);
56d9de68 262
3167abe5
NC
263 is ($c->new(2)->bpow(16),65536);
264 is ($c->bpow(2,16),65536);
265 is ($c->bpow(2,$c->new(16)),65536);
56d9de68 266
3167abe5
NC
267 is ($c->new(2**15)->brsft(1),2**14);
268 is ($c->brsft(2**15,1),2**14);
269 is ($c->brsft(2**15,$c->new(1)),2**14);
56d9de68 270
3167abe5
NC
271 is ($c->new(2**13)->blsft(1),2**14);
272 is ($c->blsft(2**13,1),2**14);
273 is ($c->blsft(2**13,$c->new(1)),2**14);
61f5c3f5
T
274 }
275
276###############################################################################
c4a6f826
PA
277# test whether operations round properly afterwards
278# These tests are not complete, since they do not exercise every "return"
61f5c3f5
T
279# statement in the op's. But heh, it's better than nothing...
280
281$x = $mbf->new('123.456');
282$y = $mbf->new('654.321');
9681bfa6
PJA
283$x->{_a} = 5; # $x->accuracy(5) would round $x straight away
284$y->{_a} = 4; # $y->accuracy(4) would round $x straight away
61f5c3f5 285
3167abe5
NC
286$z = $x + $y; is ($z,'777.8');
287$z = $y - $x; is ($z,'530.9');
288$z = $y * $x; is ($z,'80780');
289$z = $x ** 2; is ($z,'15241');
290$z = $x * $x; is ($z,'15241');
61f5c3f5 291
3167abe5
NC
292# not: $z = -$x; is ($z,'-123.46'); is ($x,'123.456');
293$z = $x->copy(); $z->{_a} = 2; $z = $z / 2; is ($z,62);
61f5c3f5 294$x = $mbf->new(123456); $x->{_a} = 4;
3167abe5 295$z = $x->copy; $z++; is ($z,123500);
61f5c3f5
T
296
297$x = $mbi->new(123456);
298$y = $mbi->new(654321);
9681bfa6
PJA
299$x->{_a} = 5; # $x->accuracy(5) would round $x straight away
300$y->{_a} = 4; # $y->accuracy(4) would round $x straight away
61f5c3f5 301
3167abe5
NC
302$z = $x + $y; is ($z,777800);
303$z = $y - $x; is ($z,530900);
304$z = $y * $x; is ($z,80780000000);
305$z = $x ** 2; is ($z,15241000000);
306# not yet: $z = -$x; is ($z,-123460); is ($x,123456);
307$z = $x->copy; $z++; is ($z,123460);
308$z = $x->copy(); $z->{_a} = 2; $z = $z / 2; is ($z,62000);
61f5c3f5
T
309
310$x = $mbi->new(123400); $x->{_a} = 4;
3167abe5 311is ($x->bnot(),-123400); # not -1234001
61f5c3f5
T
312
313# both babs() and bneg() don't need to round, since the input will already
314# be rounded (either as $x or via new($string)), and they don't change the
315# value. The two tests below peek at this by using _a (illegally) directly
3167abe5
NC
316$x = $mbi->new(-123401); $x->{_a} = 4; is ($x->babs(),123401);
317$x = $mbi->new(-123401); $x->{_a} = 4; is ($x->bneg(),123401);
61f5c3f5
T
318
319# test fdiv rounding to A and R (bug in v1.48 and maybe earlier versions)
320$mbf->round_mode('even');
3167abe5 321$x = $mbf->new('740.7')->fdiv('6',4,undef,'zero'); is ($x,'123.4');
61f5c3f5 322
990fb837 323$x = $mbi->new('123456'); $y = $mbi->new('123456'); $y->{_a} = 6;
3167abe5 324is ($x->bdiv($y),1); is ($x->{_a},6); # carried over
990fb837
RGS
325
326$x = $mbi->new('123456'); $y = $mbi->new('123456'); $x->{_a} = 6;
3167abe5 327is ($x->bdiv($y),1); is ($x->{_a},6); # carried over
990fb837
RGS
328
329$x = $mbi->new('123456'); $y = $mbi->new('223456'); $y->{_a} = 6;
3167abe5 330is ($x->bdiv($y),0); is ($x->{_a},6); # carried over
990fb837
RGS
331
332$x = $mbi->new('123456'); $y = $mbi->new('223456'); $x->{_a} = 6;
3167abe5 333is ($x->bdiv($y),0); is ($x->{_a},6); # carried over
990fb837 334
61f5c3f5 335###############################################################################
2ab5f49d
T
336# test that bop(0) does the same than bop(undef)
337
338$x = $mbf->new('1234567890');
3167abe5
NC
339is ($x->copy()->bsqrt(0),$x->copy()->bsqrt(undef));
340is ($x->copy->bsqrt(0),'35136.41828644462161665823116758077037159');
2ab5f49d 341
3167abe5 342is ($x->{_a}, undef);
2ab5f49d
T
343
344# test that bsqrt() modifies $x and does not just return something else
345# (especially under BareCalc)
346$z = $x->bsqrt();
3167abe5 347is ($z,$x); is ($x,'35136.41828644462161665823116758077037159');
2ab5f49d
T
348
349$x = $mbf->new('1.234567890123456789');
3167abe5
NC
350is ($x->copy()->bpow('0.5',0),$x->copy()->bpow('0.5',undef));
351is ($x->copy()->bpow('0.5',0),$x->copy()->bsqrt(undef));
352is ($x->copy()->bpow('2',0),'1.524157875323883675019051998750190521');
2ab5f49d
T
353
354###############################################################################
f9a08e12
JH
355# test (also under Bare) that bfac() rounds at last step
356
3167abe5
NC
357is ($mbi->new(12)->bfac(),'479001600');
358is ($mbi->new(12)->bfac(2),'480000000');
359$x = $mbi->new(12); $x->accuracy(2); is ($x->bfac(),'480000000');
360$x = $mbi->new(13); $x->accuracy(2); is ($x->bfac(),'6200000000');
361$x = $mbi->new(13); $x->accuracy(3); is ($x->bfac(),'6230000000');
362$x = $mbi->new(13); $x->accuracy(4); is ($x->bfac(),'6227000000');
f9a08e12 363# this does 1,2,3...9,10,11,12...20
3167abe5 364$x = $mbi->new(20); $x->accuracy(1); is ($x->bfac(),'2000000000000000000');
f9a08e12
JH
365
366###############################################################################
367# test bsqrt) rounding to given A/P/R (bug prior to v1.60)
3167abe5
NC
368$x = $mbi->new('123456')->bsqrt(2,undef); is ($x,'350'); # not 351
369$x = $mbi->new('3')->bsqrt(2,undef); is ($x->accuracy(),2);
f9a08e12
JH
370
371$mbi->round_mode('even'); $x = $mbi->new('126025')->bsqrt(2,undef,'+inf');
3167abe5 372is ($x,'360'); # not 355 nor 350
f9a08e12 373
3167abe5 374$x = $mbi->new('126025')->bsqrt(undef,2); is ($x,'400'); # not 355
f9a08e12
JH
375
376
377###############################################################################
61f5c3f5
T
378# test mixed arguments
379
380$x = $mbf->new(10);
381$u = $mbf->new(2.5);
382$y = $mbi->new(2);
383
3167abe5
NC
384$z = $x + $y; is ($z,12); is (ref($z),$mbf);
385$z = $x / $y; is ($z,5); is (ref($z),$mbf);
386$z = $u * $y; is ($z,5); is (ref($z),$mbf);
61f5c3f5
T
387
388$y = $mbi->new(12345);
3167abe5
NC
389$z = $u->copy()->bmul($y,2,undef,'odd'); is ($z,31000);
390$z = $u->copy()->bmul($y,3,undef,'odd'); is ($z,30900);
391$z = $u->copy()->bmul($y,undef,0,'odd'); is ($z,30863);
392$z = $u->copy()->bmul($y,undef,1,'odd'); is ($z,30863);
393$z = $u->copy()->bmul($y,undef,2,'odd'); is ($z,30860);
394$z = $u->copy()->bmul($y,undef,3,'odd'); is ($z,30900);
395$z = $u->copy()->bmul($y,undef,-1,'odd'); is ($z,30862.5);
61f5c3f5 396
56d9de68 397my $warn = ''; $SIG{__WARN__} = sub { $warn = shift; };
a0ac753d
T
398# these should no longer warn, even tho '3.17' is a NaN in BigInt (>= returns
399# now false, bug until v1.80)
3167abe5
NC
400$warn = ''; eval "\$z = 3.17 <= \$y"; is ($z, '');
401unlike ($warn, qr/^Use of uninitialized value (\$y )?(in numeric le \(<=\) |)at/);
402$warn = ''; eval "\$z = \$y >= 3.17"; is ($z, '');
403unlike ($warn, qr/^Use of uninitialized value (\$y )?(in numeric ge \(>=\) |)at/);
56d9de68
T
404
405# XXX TODO breakage:
3167abe5
NC
406# $z = $y->copy()->bmul($u,2,0,'odd'); is ($z,31000);
407# $z = $y * $u; is ($z,5); is (ref($z),$mbi);
408# $z = $y + $x; is ($z,12); is (ref($z),$mbi);
409# $z = $y / $x; is ($z,0); is (ref($z),$mbi);
61f5c3f5
T
410
411###############################################################################
412# rounding in bdiv with fallback and already set A or P
413
56d9de68
T
414{
415 no strict 'refs';
416 ${"$mbf\::accuracy"} = undef;
417 ${"$mbf\::precision"} = undef;
418 ${"$mbf\::div_scale"} = 40;
419}
61f5c3f5 420
56d9de68 421 $x = $mbf->new(10); $x->{_a} = 4;
3167abe5
NC
422 is ($x->bdiv(3),'3.333');
423 is ($x->{_a},4); # set's it since no fallback
61f5c3f5
T
424
425$x = $mbf->new(10); $x->{_a} = 4; $y = $mbf->new(3);
3167abe5
NC
426is ($x->bdiv($y),'3.333');
427is ($x->{_a},4); # set's it since no fallback
61f5c3f5
T
428
429# rounding to P of x
430$x = $mbf->new(10); $x->{_p} = -2;
3167abe5 431is ($x->bdiv(3),'3.33');
61f5c3f5
T
432
433# round in div with requested P
434$x = $mbf->new(10);
3167abe5 435is ($x->bdiv(3,undef,-2),'3.33');
61f5c3f5
T
436
437# round in div with requested P greater than fallback
56d9de68
T
438{
439 no strict 'refs';
440 ${"$mbf\::div_scale"} = 5;
441 $x = $mbf->new(10);
3167abe5 442 is ($x->bdiv(3,undef,-8),'3.33333333');
56d9de68
T
443 ${"$mbf\::div_scale"} = 40;
444}
61f5c3f5
T
445
446$x = $mbf->new(10); $y = $mbf->new(3); $y->{_a} = 4;
3167abe5
NC
447is ($x->bdiv($y),'3.333');
448is ($x->{_a},4); is ($y->{_a},4); # set's it since no fallback
449is ($x->{_p}, undef); is ($y->{_p}, undef);
61f5c3f5
T
450
451# rounding to P of y
452$x = $mbf->new(10); $y = $mbf->new(3); $y->{_p} = -2;
3167abe5
NC
453is ($x->bdiv($y),'3.33');
454is ($x->{_p},-2);
455 is ($y->{_p},-2);
456is ($x->{_a}, undef); is ($y->{_a}, undef);
61f5c3f5
T
457
458###############################################################################
459# test whether bround(-n) fails in MBF (undocumented in MBI)
460eval { $x = $mbf->new(1); $x->bround(-2); };
3167abe5 461like ($@, qr/^bround\(\) needs positive accuracy/);
61f5c3f5
T
462
463# test whether rounding to higher accuracy is no-op
464$x = $mbf->new(1); $x->{_a} = 4;
3167abe5 465is ($x,'1.000');
61f5c3f5 466$x->bround(6); # must be no-op
3167abe5
NC
467is ($x->{_a},4);
468is ($x,'1.000');
61f5c3f5
T
469
470$x = $mbi->new(1230); $x->{_a} = 3;
3167abe5 471is ($x,'1230');
61f5c3f5 472$x->bround(6); # must be no-op
3167abe5
NC
473is ($x->{_a},3);
474is ($x,'1230');
61f5c3f5
T
475
476# bround(n) should set _a
477$x->bround(2); # smaller works
3167abe5
NC
478is ($x,'1200');
479is ($x->{_a},2);
61f5c3f5
T
480
481# bround(-n) is undocumented and only used by MBF
482# bround(-n) should set _a
483$x = $mbi->new(12345);
484$x->bround(-1);
3167abe5
NC
485is ($x,'12300');
486is ($x->{_a},4);
61f5c3f5
T
487
488# bround(-n) should set _a
489$x = $mbi->new(12345);
490$x->bround(-2);
3167abe5
NC
491is ($x,'12000');
492is ($x->{_a},3);
61f5c3f5
T
493
494# bround(-n) should set _a
495$x = $mbi->new(12345); $x->{_a} = 5;
496$x->bround(-3);
3167abe5
NC
497is ($x,'10000');
498is ($x->{_a},2);
61f5c3f5
T
499
500# bround(-n) should set _a
501$x = $mbi->new(12345); $x->{_a} = 5;
502$x->bround(-4);
3167abe5
NC
503is ($x,'0');
504is ($x->{_a},1);
61f5c3f5
T
505
506# bround(-n) should be noop if n too big
507$x = $mbi->new(12345);
508$x->bround(-5);
3167abe5
NC
509is ($x,'0'); # scale to "big" => 0
510is ($x->{_a},0);
61f5c3f5
T
511
512# bround(-n) should be noop if n too big
513$x = $mbi->new(54321);
514$x->bround(-5);
3167abe5
NC
515is ($x,'100000'); # used by MBF to round 0.0054321 at 0.0_6_00000
516is ($x->{_a},0);
61f5c3f5
T
517
518# bround(-n) should be noop if n too big
519$x = $mbi->new(54321); $x->{_a} = 5;
520$x->bround(-6);
3167abe5
NC
521is ($x,'100000'); # no-op
522is ($x->{_a},0);
61f5c3f5
T
523
524# bround(n) should set _a
525$x = $mbi->new(12345); $x->{_a} = 5;
526$x->bround(5); # must be no-op
3167abe5
NC
527is ($x,'12345');
528is ($x->{_a},5);
61f5c3f5
T
529
530# bround(n) should set _a
531$x = $mbi->new(12345); $x->{_a} = 5;
532$x->bround(6); # must be no-op
3167abe5 533is ($x,'12345');
61f5c3f5 534
3167abe5
NC
535$x = $mbf->new('0.0061'); $x->bfround(-2); is ($x,'0.01');
536$x = $mbf->new('0.004'); $x->bfround(-2); is ($x,'0.00');
537$x = $mbf->new('0.005'); $x->bfround(-2); is ($x,'0.00');
b3abae2a 538
3167abe5
NC
539$x = $mbf->new('12345'); $x->bfround(2); is ($x,'12340');
540$x = $mbf->new('12340'); $x->bfround(2); is ($x,'12340');
61f5c3f5
T
541
542# MBI::bfround should clear A for negative P
543$x = $mbi->new('1234'); $x->accuracy(3); $x->bfround(-2);
3167abe5 544is ($x->{_a}, undef);
61f5c3f5 545
9b924220
RGS
546# test that bfround() and bround() work with large numbers
547
548$x = $mbf->new(1)->bdiv(5678,undef,-63);
3167abe5 549is ($x, '0.000176118351532229658330398027474462839027826699542092286016203');
9b924220
RGS
550
551$x = $mbf->new(1)->bdiv(5678,undef,-90);
3167abe5 552is ($x, '0.000176118351532229658330398027474462839027826699542092286016202888340965128566396618527651');
9b924220
RGS
553
554$x = $mbf->new(1)->bdiv(5678,80);
3167abe5 555is ($x, '0.00017611835153222965833039802747446283902782669954209228601620288834096512856639662');
9b924220 556
61f5c3f5
T
557###############################################################################
558# rounding with already set precision/accuracy
559
560$x = $mbf->new(1); $x->{_p} = -5;
3167abe5 561is ($x,'1.00000');
61f5c3f5
T
562
563# further rounding donw
3167abe5
NC
564is ($x->bfround(-2),'1.00');
565is ($x->{_p},-2);
61f5c3f5
T
566
567$x = $mbf->new(12345); $x->{_a} = 5;
3167abe5
NC
568is ($x->bround(2),'12000');
569is ($x->{_a},2);
61f5c3f5
T
570
571$x = $mbf->new('1.2345'); $x->{_a} = 5;
3167abe5
NC
572is ($x->bround(2),'1.2');
573is ($x->{_a},2);
61f5c3f5
T
574
575# mantissa/exponent format and A/P
576$x = $mbf->new('12345.678'); $x->accuracy(4);
3167abe5 577is ($x,'12350'); is ($x->{_a},4); is ($x->{_p}, undef);
9b924220 578
3167abe5
NC
579#is ($x->{_m}->{_a}, undef); is ($x->{_e}->{_a}, undef);
580#is ($x->{_m}->{_p}, undef); is ($x->{_e}->{_p}, undef);
61f5c3f5
T
581
582# check for no A/P in case of fallback
583# result
584$x = $mbf->new(100) / 3;
3167abe5 585is ($x->{_a}, undef); is ($x->{_p}, undef);
61f5c3f5 586
f603091d 587# result & remainder
61f5c3f5 588$x = $mbf->new(100) / 3; ($x,$y) = $x->bdiv(3);
3167abe5
NC
589is ($x->{_a}, undef); is ($x->{_p}, undef);
590is ($y->{_a}, undef); is ($y->{_p}, undef);
61f5c3f5
T
591
592###############################################################################
9681bfa6 593# math with two numbers with different A and P
61f5c3f5
T
594
595$x = $mbf->new(12345); $x->accuracy(4); # '12340'
596$y = $mbf->new(12345); $y->accuracy(2); # '12000'
3167abe5 597is ($x+$y,24000); # 12340+12000=> 24340 => 24000
61f5c3f5
T
598
599$x = $mbf->new(54321); $x->accuracy(4); # '12340'
600$y = $mbf->new(12345); $y->accuracy(3); # '12000'
3167abe5 601is ($x-$y,42000); # 54320+12300=> 42020 => 42000
61f5c3f5
T
602
603$x = $mbf->new('1.2345'); $x->precision(-2); # '1.23'
604$y = $mbf->new('1.2345'); $y->precision(-4); # '1.2345'
3167abe5 605is ($x+$y,'2.46'); # 1.2345+1.2300=> 2.4645 => 2.46
61f5c3f5
T
606
607###############################################################################
608# round should find and use proper class
609
610#$x = Foo->new();
3167abe5
NC
611#is ($x->round($Foo::accuracy),'a' x $Foo::accuracy);
612#is ($x->round(undef,$Foo::precision),'p' x $Foo::precision);
613#is ($x->bfround($Foo::precision),'p' x $Foo::precision);
614#is ($x->bround($Foo::accuracy),'a' x $Foo::accuracy);
61f5c3f5
T
615
616###############################################################################
617# find out whether _find_round_parameters is doing what's it's supposed to do
56d9de68
T
618
619{
620 no strict 'refs';
621 ${"$mbi\::accuracy"} = undef;
622 ${"$mbi\::precision"} = undef;
623 ${"$mbi\::div_scale"} = 40;
624 ${"$mbi\::round_mode"} = 'odd';
625}
626
61f5c3f5
T
627$x = $mbi->new(123);
628my @params = $x->_find_round_parameters();
3167abe5 629is (scalar @params,1); # nothing to round
61f5c3f5
T
630
631@params = $x->_find_round_parameters(1);
3167abe5
NC
632is (scalar @params,4); # a=1
633is ($params[0],$x); # self
634is ($params[1],1); # a
635is ($params[2], undef); # p
636is ($params[3],'odd'); # round_mode
61f5c3f5
T
637
638@params = $x->_find_round_parameters(undef,2);
3167abe5
NC
639is (scalar @params,4); # p=2
640is ($params[0],$x); # self
641is ($params[1], undef); # a
642is ($params[2],2); # p
643is ($params[3],'odd'); # round_mode
61f5c3f5
T
644
645eval { @params = $x->_find_round_parameters(undef,2,'foo'); };
3167abe5 646like ($@, qr/^Unknown round mode 'foo'/);
61f5c3f5
T
647
648@params = $x->_find_round_parameters(undef,2,'+inf');
3167abe5
NC
649is (scalar @params,4); # p=2
650is ($params[0],$x); # self
651is ($params[1], undef); # a
652is ($params[2],2); # p
653is ($params[3],'+inf'); # round_mode
61f5c3f5
T
654
655@params = $x->_find_round_parameters(2,-2,'+inf');
3167abe5
NC
656is (scalar @params,1); # error, A and P defined
657is ($params[0],$x); # self
61f5c3f5 658
56d9de68
T
659{
660 no strict 'refs';
661 ${"$mbi\::accuracy"} = 1;
662 @params = $x->_find_round_parameters(undef,-2);
3167abe5
NC
663 is (scalar @params,1); # error, A and P defined
664 is ($params[0],$x); # self
665 is ($x->is_nan(),1); # and must be NaN
56d9de68
T
666
667 ${"$mbi\::accuracy"} = undef;
668 ${"$mbi\::precision"} = 1;
669 @params = $x->_find_round_parameters(1,undef);
3167abe5
NC
670 is (scalar @params,1); # error, A and P defined
671 is ($params[0],$x); # self
672 is ($x->is_nan(),1); # and must be NaN
56d9de68
T
673
674 ${"$mbi\::precision"} = undef; # reset
675}
61f5c3f5
T
676
677###############################################################################
678# test whether bone/bzero take additional A & P, or reset it etc
679
f9a08e12 680foreach my $c ($mbi,$mbf)
61f5c3f5 681 {
3167abe5
NC
682 $x = $c->new(2)->bzero(); is ($x->{_a}, undef); is ($x->{_p}, undef);
683 $x = $c->new(2)->bone(); is ($x->{_a}, undef); is ($x->{_p}, undef);
684 $x = $c->new(2)->binf(); is ($x->{_a}, undef); is ($x->{_p}, undef);
685 $x = $c->new(2)->bnan(); is ($x->{_a}, undef); is ($x->{_p}, undef);
61f5c3f5 686
f9a08e12 687 $x = $c->new(2); $x->{_a} = 1; $x->{_p} = 2; $x->bnan();
3167abe5 688 is ($x->{_a}, undef); is ($x->{_p}, undef);
f9a08e12 689 $x = $c->new(2); $x->{_a} = 1; $x->{_p} = 2; $x->binf();
3167abe5 690 is ($x->{_a}, undef); is ($x->{_p}, undef);
61f5c3f5 691
3167abe5
NC
692 $x = $c->new(2,1); is ($x->{_a},1); is ($x->{_p}, undef);
693 $x = $c->new(2,undef,1); is ($x->{_a}, undef); is ($x->{_p},1);
f9a08e12 694
3167abe5
NC
695 $x = $c->new(2,1)->bzero(); is ($x->{_a},1); is ($x->{_p}, undef);
696 $x = $c->new(2,undef,1)->bzero(); is ($x->{_a}, undef); is ($x->{_p},1);
f9a08e12 697
3167abe5
NC
698 $x = $c->new(2,1)->bone(); is ($x->{_a},1); is ($x->{_p}, undef);
699 $x = $c->new(2,undef,1)->bone(); is ($x->{_a}, undef); is ($x->{_p},1);
f9a08e12 700
3167abe5
NC
701 $x = $c->new(2); $x->bone('+',2,undef); is ($x->{_a},2); is ($x->{_p}, undef);
702 $x = $c->new(2); $x->bone('+',undef,2); is ($x->{_a}, undef); is ($x->{_p},2);
703 $x = $c->new(2); $x->bone('-',2,undef); is ($x->{_a},2); is ($x->{_p}, undef);
704 $x = $c->new(2); $x->bone('-',undef,2); is ($x->{_a}, undef); is ($x->{_p},2);
61f5c3f5 705
3167abe5
NC
706 $x = $c->new(2); $x->bzero(2,undef); is ($x->{_a},2); is ($x->{_p}, undef);
707 $x = $c->new(2); $x->bzero(undef,2); is ($x->{_a}, undef); is ($x->{_p},2);
f9a08e12
JH
708 }
709
710###############################################################################
711# test whether bone/bzero honour globals
61f5c3f5 712
f9a08e12
JH
713for my $c ($mbi,$mbf)
714 {
715 $c->accuracy(2);
3167abe5
NC
716 $x = $c->bone(); is ($x->accuracy(),2);
717 $x = $c->bzero(); is ($x->accuracy(),2);
f9a08e12
JH
718 $c->accuracy(undef);
719
720 $c->precision(-2);
3167abe5
NC
721 $x = $c->bone(); is ($x->precision(),-2);
722 $x = $c->bzero(); is ($x->precision(),-2);
f9a08e12 723 $c->precision(undef);
61f5c3f5
T
724 }
725
726###############################################################################
727# check whether mixing A and P creates a NaN
728
729# new with set accuracy/precision and with parameters
56d9de68
T
730{
731 no strict 'refs';
732 foreach my $c ($mbi,$mbf)
733 {
3167abe5 734 is ($c->new(123,4,-3),'NaN'); # with parameters
56d9de68
T
735 ${"$c\::accuracy"} = 42;
736 ${"$c\::precision"} = 2;
3167abe5 737 is ($c->new(123),'NaN'); # with globals
56d9de68
T
738 ${"$c\::accuracy"} = undef;
739 ${"$c\::precision"} = undef;
740 }
741}
61f5c3f5
T
742
743# binary ops
744foreach my $class ($mbi,$mbf)
745 {
746 foreach (qw/add sub mul pow mod/)
747 #foreach (qw/add sub mul div pow mod/)
748 {
749 my $try = "my \$x = $class->new(1234); \$x->accuracy(5); ";
750 $try .= "my \$y = $class->new(12); \$y->precision(-3); ";
751 $try .= "\$x->b$_(\$y);";
752 $rc = eval $try;
3167abe5 753 print "# Tried: '$try'\n" if !is ($rc, 'NaN');
61f5c3f5
T
754 }
755 }
756
757# unary ops
758foreach (qw/new bsqrt/)
759 {
760 my $try = 'my $x = $mbi->$_(1234,5,-3); ';
761 $rc = eval $try;
3167abe5 762 print "# Tried: '$try'\n" if !is ($rc, 'NaN');
61f5c3f5
T
763 }
764
28df3e88
JH
765# see if $x->bsub(0) and $x->badd(0) really round
766foreach my $class ($mbi,$mbf)
767 {
768 $x = $class->new(123); $class->accuracy(2); $x->bsub(0);
3167abe5 769 is ($x,120);
28df3e88
JH
770 $class->accuracy(undef);
771 $x = $class->new(123); $class->accuracy(2); $x->badd(0);
3167abe5 772 is ($x,120);
28df3e88
JH
773 $class->accuracy(undef);
774 }
b3abae2a 775
61f5c3f5
T
776###############################################################################
777# test whether shortcuts returning zero/one preserve A and P
778
779my ($ans1,$f,$a,$p,$xp,$yp,$xa,$ya,$try,$ans,@args);
b3abae2a 780my $CALC = Math::BigInt->config()->{lib};
61f5c3f5
T
781while (<DATA>)
782 {
f7f86b73 783 $_ =~ s/[\n\r]//g; # remove newlines
61f5c3f5
T
784 next if /^\s*(#|$)/; # skip comments and empty lines
785 if (s/^&//)
786 {
787 $f = $_; next; # function
788 }
789 @args = split(/:/,$_,99);
790 my $ans = pop(@args);
791
792 ($x,$xa,$xp) = split (/,/,$args[0]);
793 $xa = $xa || ''; $xp = $xp || '';
794 $try = "\$x = $mbi->new('$x'); ";
795 $try .= "\$x->accuracy($xa); " if $xa ne '';
796 $try .= "\$x->precision($xp); " if $xp ne '';
797
798 ($y,$ya,$yp) = split (/,/,$args[1]);
799 $ya = $ya || ''; $yp = $yp || '';
800 $try .= "\$y = $mbi->new('$y'); ";
801 $try .= "\$y->accuracy($ya); " if $ya ne '';
802 $try .= "\$y->precision($yp); " if $yp ne '';
803
804 $try .= "\$x->$f(\$y);";
805
9b924220 806 # print "trying $try\n";
61f5c3f5
T
807 $rc = eval $try;
808 # convert hex/binary targets to decimal
809 if ($ans =~ /^(0x0x|0b0b)/)
810 {
811 $ans =~ s/^0[xb]//;
812 $ans = $mbi->new($ans)->bstr();
813 }
3167abe5 814 print "# Tried: '$try'\n" if !is ($rc, $ans);
61f5c3f5
T
815 # check internal state of number objects
816 is_valid($rc,$f) if ref $rc;
817
818 # now check whether A and P are set correctly
819 # only one of $a or $p will be set (no crossing here)
820 $a = $xa || $ya; $p = $xp || $yp;
821
822 # print "Check a=$a p=$p\n";
b3abae2a 823 # print "# Tried: '$try'\n";
f9a08e12
JH
824 if ($a ne '')
825 {
3167abe5 826 if (!(is ($x->{_a}, $a) && is ($x->{_p}, undef)))
f9a08e12
JH
827 {
828 print "# Check: A=$a and P=undef\n";
829 print "# Tried: '$try'\n";
830 }
831 }
832 if ($p ne '')
833 {
3167abe5 834 if (!(is ($x->{_p}, $p) && is($x->{_a}, undef)))
f9a08e12
JH
835 {
836 print "# Check: A=undef and P=$p\n";
837 print "# Tried: '$try'\n";
838 }
839 }
61f5c3f5
T
840 }
841
842# all done
8431;
844
845###############################################################################
61f5c3f5
T
846# sub to check validity of a BigInt internally, to ensure that no op leaves a
847# number object in an invalid state (f.i. "-0")
848
849sub is_valid
850 {
851 my ($x,$f) = @_;
852
853 my $e = 0; # error?
854 # ok as reference?
855 $e = 'Not a reference' if !ref($x);
856
857 # has ok sign?
858 $e = "Illegal sign $x->{sign} (expected: '+', '-', '-inf', '+inf' or 'NaN'"
859 if $e eq '0' && $x->{sign} !~ /^(\+|-|\+inf|-inf|NaN)$/;
860
861 $e = "-0 is invalid!" if $e ne '0' && $x->{sign} eq '-' && $x == 0;
862 $e = $CALC->_check($x->{value}) if $e eq '0';
863
864 # test done, see if error did crop up
3167abe5 865 is (1,1), return if ($e eq '0');
61f5c3f5 866
3167abe5 867 is (1,$e." after op '$f'");
61f5c3f5
T
868 }
869
870# format is:
871# x,A,P:x,A,P:result
872# 123,,3 means 123 with precision 3 (A is undef)
873# the A or P of the result is calculated automatically
874__DATA__
875&badd
61f5c3f5
T
876123,,:123,,:246
877123,3,:0,,:123
878123,,-3:0,,:123
879123,,:0,3,:123
880123,,:0,,-3:123
881&bmul
882123,,:1,,:123
883123,3,:0,,:0
884123,,-3:0,,:0
885123,,:0,3,:0
886123,,:0,,-3:0
887123,3,:1,,:123
888123,,-3:1,,:123
889123,,:1,3,:123
890123,,:1,,-3:123
8911,3,:123,,:123
8921,,-3:123,,:123
8931,,:123,3,:123
8941,,:123,,-3:123
895&bdiv
896123,,:1,,:123
897123,4,:1,,:123
898123,,:1,4,:123
899123,,:1,,-4:123
900123,,-4:1,,:123
9011,4,:123,,:0
9021,,:123,4,:0
9031,,:123,,-4:0
9041,,-4:123,,:0
f9a08e12
JH
905&band
9061,,:3,,:1
9071234,1,:0,,:0
9081234,,:0,1,:0
9091234,,-1:0,,:0
9101234,,:0,,-1:0
9110xFF,,:0x10,,:0x0x10
9120xFF,2,:0xFF,,:250
9130xFF,,:0xFF,2,:250
9140xFF,,1:0xFF,,:250
9150xFF,,:0xFF,,1:250
916&bxor
9171,,:3,,:2
9181234,1,:0,,:1000
9191234,,:0,1,:1000
9201234,,3:0,,:1000
9211234,,:0,,3:1000
9220xFF,,:0x10,,:239
923# 250 ^ 255 => 5
9240xFF,2,:0xFF,,:5
9250xFF,,:0xFF,2,:5
9260xFF,,1:0xFF,,:5
9270xFF,,:0xFF,,1:5
928# 250 ^ 4095 = 3845 => 3800
9290xFF,2,:0xFFF,,:3800
930# 255 ^ 4100 = 4347 => 4300
9310xFF,,:0xFFF,2,:4300
9320xFF,,2:0xFFF,,:3800
933# 255 ^ 4100 = 10fb => 4347 => 4300
9340xFF,,:0xFFF,,2:4300
935&bior
9361,,:3,,:3
9371234,1,:0,,:1000
9381234,,:0,1,:1000
9391234,,3:0,,:1000
9401234,,:0,,3:1000
9410xFF,,:0x10,,:0x0xFF
942# FF | FA = FF => 250
943250,2,:0xFF,,:250
9440xFF,,:250,2,:250
9450xFF,,1:0xFF,,:250
9460xFF,,:0xFF,,1:250
947&bpow
9482,,:3,,:8
9492,,:0,,:1
9502,2,:0,,:1
9512,,:0,2,:1