Dynaloading for POSIX-BC from Thomas Dorner.
[p5sagit/p5-mst-13.2.git] / lib / Math / BigInt / t / mbimbf.t
CommitLineData
58cde26e 1#!/usr/bin/perl -w
2
ee15d750 3# test rounding, accuracy, precicion and fallback, round_mode and mixing
4# of classes
58cde26e 5
dccbb853 6# Make sure you always quote any bare floating-point values, lest 123.46 will
7# be stringified to 123.4599999999 due to limited float prevision.
8
58cde26e 9use strict;
10use Test;
11
12BEGIN
13 {
14 $| = 1;
15 # chdir 't' if -d 't';
16 unshift @INC, '../lib'; # for running manually
dccbb853 17 plan tests => 254;
58cde26e 18 }
19
ee15d750 20# for finding out whether round finds correct class
21package Foo;
22
23use Math::BigInt;
24use vars qw/@ISA $precision $accuracy $div_scale $round_mode/;
25@ISA = qw/Math::BigInt/;
26
27$precision = 6;
28$accuracy = 8;
29$div_scale = 5;
30$round_mode = 'odd';
31
32sub new
33 {
34 my $class = shift;
35 my $self = { _a => undef, _p => undef, value => 5 };
36 bless $self, $class;
37 }
38
39sub bstr
40 {
41 my $self = shift;
42
43 return "$self->{value}";
44 }
45
46# these will be called with the rounding precision or accuracy, depending on
47# class
48sub bround
49 {
50 my ($self,$a,$r) = @_;
51 $self->{value} = 'a' x $a;
52 return $self;
53 }
54
55sub bnorm
56 {
57 my $self = shift;
58 return $self;
59 }
60
61sub bfround
62 {
63 my ($self,$p,$r) = @_;
64 $self->{value} = 'p' x $p;
65 return $self;
66 }
67
68package main;
69
58cde26e 70use Math::BigInt;
71use Math::BigFloat;
72
73my ($x,$y,$z,$u);
74
75###############################################################################
76# test defaults and set/get
77
78ok_undef ($Math::BigInt::accuracy);
79ok_undef ($Math::BigInt::precision);
dccbb853 80ok_undef (Math::BigInt::accuracy());
81ok_undef (Math::BigInt::precision());
ee15d750 82ok_undef (Math::BigInt->accuracy());
83ok_undef (Math::BigInt->precision());
58cde26e 84ok ($Math::BigInt::div_scale,40);
ee15d750 85ok (Math::BigInt::div_scale(),40);
86ok ($Math::BigInt::round_mode,'even');
58cde26e 87ok (Math::BigInt::round_mode(),'even');
dccbb853 88ok (Math::BigInt->round_mode(),'even');
58cde26e 89
90ok_undef ($Math::BigFloat::accuracy);
91ok_undef ($Math::BigFloat::precision);
dccbb853 92ok_undef (Math::BigFloat::accuracy());
93ok_undef (Math::BigFloat::accuracy());
94ok_undef (Math::BigFloat->precision());
ee15d750 95ok_undef (Math::BigFloat->precision());
58cde26e 96ok ($Math::BigFloat::div_scale,40);
ee15d750 97ok (Math::BigFloat::div_scale(),40);
98ok ($Math::BigFloat::round_mode,'even');
99ok (Math::BigFloat::round_mode(),'even');
dccbb853 100ok (Math::BigFloat->round_mode(),'even');
101
102$x = eval 'Math::BigInt->round_mode("huhmbi");';
103ok ($@ =~ /^Unknown round mode huhmbi at/);
104
105$x = eval 'Math::BigFloat->round_mode("huhmbf");';
106ok ($@ =~ /^Unknown round mode huhmbf at/);
ee15d750 107
108# accessors
109foreach my $class (qw/Math::BigInt Math::BigFloat/)
110 {
111 ok_undef ($class->accuracy());
112 ok_undef ($class->precision());
113 ok ($class->round_mode(),'even');
114 ok ($class->div_scale(),40);
115
116 ok ($class->div_scale(20),20);
117 $class->div_scale(40); ok ($class->div_scale(),40);
118
119 ok ($class->round_mode('odd'),'odd');
120 $class->round_mode('even'); ok ($class->round_mode(),'even');
121
122 ok ($class->accuracy(2),2);
123 $class->accuracy(3); ok ($class->accuracy(),3);
124 ok_undef ($class->accuracy(undef));
125
126 ok ($class->precision(2),2);
127 ok ($class->precision(-2),-2);
128 $class->precision(3); ok ($class->precision(),3);
129 ok_undef ($class->precision(undef));
130 }
58cde26e 131
132# accuracy
133foreach (qw/5 42 -1 0/)
134 {
135 ok ($Math::BigFloat::accuracy = $_,$_);
136 ok ($Math::BigInt::accuracy = $_,$_);
137 }
138ok_undef ($Math::BigFloat::accuracy = undef);
139ok_undef ($Math::BigInt::accuracy = undef);
140
141# precision
142foreach (qw/5 42 -1 0/)
143 {
144 ok ($Math::BigFloat::precision = $_,$_);
145 ok ($Math::BigInt::precision = $_,$_);
146 }
147ok_undef ($Math::BigFloat::precision = undef);
148ok_undef ($Math::BigInt::precision = undef);
149
150# fallback
151foreach (qw/5 42 1/)
152 {
153 ok ($Math::BigFloat::div_scale = $_,$_);
154 ok ($Math::BigInt::div_scale = $_,$_);
155 }
156# illegal values are possible for fallback due to no accessor
157
158# round_mode
159foreach (qw/odd even zero trunc +inf -inf/)
160 {
ee15d750 161 ok ($Math::BigFloat::round_mode = $_,$_);
162 ok ($Math::BigInt::round_mode = $_,$_);
58cde26e 163 }
ee15d750 164$Math::BigFloat::round_mode = 'zero';
165ok ($Math::BigFloat::round_mode,'zero');
166ok ($Math::BigInt::round_mode,'-inf'); # from above
58cde26e 167
168$Math::BigInt::accuracy = undef;
169$Math::BigInt::precision = undef;
170# local copies
dccbb853 171$x = Math::BigFloat->new('123.456');
58cde26e 172ok_undef ($x->accuracy());
173ok ($x->accuracy(5),5);
174ok_undef ($x->accuracy(undef),undef);
175ok_undef ($x->precision());
176ok ($x->precision(5),5);
177ok_undef ($x->precision(undef),undef);
178
179# see if MBF changes MBIs values
180ok ($Math::BigInt::accuracy = 42,42);
181ok ($Math::BigFloat::accuracy = 64,64);
182ok ($Math::BigInt::accuracy,42); # should be still 42
183ok ($Math::BigFloat::accuracy,64); # should be still 64
184
185###############################################################################
186# see if creating a number under set A or P will round it
187
188$Math::BigInt::accuracy = 4;
189$Math::BigInt::precision = 3;
190
191ok (Math::BigInt->new(123456),123500); # with A
192$Math::BigInt::accuracy = undef;
193ok (Math::BigInt->new(123456),123000); # with P
194
195$Math::BigFloat::accuracy = 4;
196$Math::BigFloat::precision = -1;
197$Math::BigInt::precision = undef;
198
dccbb853 199ok (Math::BigFloat->new('123.456'),'123.5'); # with A
58cde26e 200$Math::BigFloat::accuracy = undef;
dccbb853 201ok (Math::BigFloat->new('123.456'),'123.5'); # with P from MBF, not MBI!
58cde26e 202
203$Math::BigFloat::precision = undef;
204
205###############################################################################
206# see if setting accuracy/precision actually rounds the number
207
dccbb853 208$x = Math::BigFloat->new('123.456'); $x->accuracy(4); ok ($x,'123.5');
209$x = Math::BigFloat->new('123.456'); $x->precision(-2); ok ($x,'123.46');
58cde26e 210
dccbb853 211$x = Math::BigInt->new('123456'); $x->accuracy(4); ok ($x,123500);
212$x = Math::BigInt->new('123456'); $x->precision(2); ok ($x,123500);
58cde26e 213
214###############################################################################
215# test actual rounding via round()
216
dccbb853 217$x = Math::BigFloat->new('123.456');
218ok ($x->copy()->round(5,2),'123.46');
219ok ($x->copy()->round(4,2),'123.5');
220ok ($x->copy()->round(undef,-2),'123.46');
58cde26e 221ok ($x->copy()->round(undef,2),100);
222
dccbb853 223$x = Math::BigFloat->new('123.45000');
224ok ($x->copy()->round(undef,-1,'odd'),'123.5');
58cde26e 225
226# see if rounding is 'sticky'
dccbb853 227$x = Math::BigFloat->new('123.4567');
58cde26e 228$y = $x->copy()->bround(); # no-op since nowhere A or P defined
229
230ok ($y,123.4567);
231$y = $x->copy()->round(5,2);
232ok ($y->accuracy(),5);
233ok_undef ($y->precision()); # A has precedence, so P still unset
234$y = $x->copy()->round(undef,2);
235ok ($y->precision(),2);
236ok_undef ($y->accuracy()); # P has precedence, so A still unset
237
ee15d750 238# see if setting A clears P and vice versa
dccbb853 239$x = Math::BigFloat->new('123.4567');
240ok ($x,'123.4567');
ee15d750 241ok ($x->accuracy(4),4);
242ok ($x->precision(-2),-2); # clear A
243ok_undef ($x->accuracy());
244
dccbb853 245$x = Math::BigFloat->new('123.4567');
246ok ($x,'123.4567');
ee15d750 247ok ($x->precision(-2),-2);
248ok ($x->accuracy(4),4); # clear P
249ok_undef ($x->precision());
250
58cde26e 251# does copy work?
252$x = Math::BigFloat->new(123.456); $x->accuracy(4); $x->precision(2);
ee15d750 253$z = $x->copy(); ok_undef ($z->accuracy(),undef); ok ($z->precision(),2);
58cde26e 254
255###############################################################################
256# test wether operations round properly afterwards
257# These tests are not complete, since they do not excercise every "return"
258# statement in the op's. But heh, it's better than nothing...
259
dccbb853 260$x = Math::BigFloat->new('123.456');
261$y = Math::BigFloat->new('654.321');
58cde26e 262$x->{_a} = 5; # $x->accuracy(5) would round $x straightaway
263$y->{_a} = 4; # $y->accuracy(4) would round $x straightaway
264
dccbb853 265$z = $x + $y; ok ($z,'777.8');
266$z = $y - $x; ok ($z,'530.9');
267$z = $y * $x; ok ($z,'80780');
268$z = $x ** 2; ok ($z,'15241');
269$z = $x * $x; ok ($z,'15241');
ee15d750 270
dccbb853 271# not: $z = -$x; ok ($z,'-123.46'); ok ($x,'123.456');
58cde26e 272$z = $x->copy(); $z->{_a} = 2; $z = $z / 2; ok ($z,62);
273$x = Math::BigFloat->new(123456); $x->{_a} = 4;
274$z = $x->copy; $z++; ok ($z,123500);
275
276$x = Math::BigInt->new(123456);
277$y = Math::BigInt->new(654321);
278$x->{_a} = 5; # $x->accuracy(5) would round $x straightaway
279$y->{_a} = 4; # $y->accuracy(4) would round $x straightaway
280
281$z = $x + $y; ok ($z,777800);
282$z = $y - $x; ok ($z,530900);
283$z = $y * $x; ok ($z,80780000000);
284$z = $x ** 2; ok ($z,15241000000);
285# not yet: $z = -$x; ok ($z,-123460); ok ($x,123456);
286$z = $x->copy; $z++; ok ($z,123460);
287$z = $x->copy(); $z->{_a} = 2; $z = $z / 2; ok ($z,62000);
288
ee15d750 289$x = Math::BigInt->new(123400); $x->{_a} = 4;
290ok ($x->bnot(),-123400); # not -1234001
291
292# both babs() and bneg() don't need to round, since the input will already
293# be rounded (either as $x or via new($string)), and they don't change the
294# value
295# The two tests below peek at this by using _a illegally
296$x = Math::BigInt->new(-123401); $x->{_a} = 4;
297ok ($x->babs(),123401);
298$x = Math::BigInt->new(-123401); $x->{_a} = 4;
299ok ($x->bneg(),123401);
300
58cde26e 301###############################################################################
302# test mixed arguments
303
304$x = Math::BigFloat->new(10);
305$u = Math::BigFloat->new(2.5);
306$y = Math::BigInt->new(2);
307
308$z = $x + $y; ok ($z,12); ok (ref($z),'Math::BigFloat');
309$z = $x / $y; ok ($z,5); ok (ref($z),'Math::BigFloat');
310$z = $u * $y; ok ($z,5); ok (ref($z),'Math::BigFloat');
311
312$y = Math::BigInt->new(12345);
313$z = $u->copy()->bmul($y,2,0,'odd'); ok ($z,31000);
314$z = $u->copy()->bmul($y,3,0,'odd'); ok ($z,30900);
315$z = $u->copy()->bmul($y,undef,0,'odd'); ok ($z,30863);
316$z = $u->copy()->bmul($y,undef,1,'odd'); ok ($z,30860);
317$z = $u->copy()->bmul($y,undef,-1,'odd'); ok ($z,30862.5);
318
319# breakage:
320# $z = $y->copy()->bmul($u,2,0,'odd'); ok ($z,31000);
321# $z = $y * $u; ok ($z,5); ok (ref($z),'Math::BigInt');
322# $z = $y + $x; ok ($z,12); ok (ref($z),'Math::BigInt');
323# $z = $y / $x; ok ($z,0); ok (ref($z),'Math::BigInt');
324
ee15d750 325###############################################################################
326# rounding in bdiv with fallback and already set A or P
327
328$Math::BigFloat::accuracy = undef;
329$Math::BigFloat::precision = undef;
330$Math::BigFloat::div_scale = 40;
331
332$x = Math::BigFloat->new(10); $x->{_a} = 4;
333ok ($x->bdiv(3),'3.333');
334ok ($x->{_a},4); # set's it since no fallback
335
336$x = Math::BigFloat->new(10); $x->{_a} = 4; $y = Math::BigFloat->new(3);
337ok ($x->bdiv($y),'3.333');
338ok ($x->{_a},4); # set's it since no fallback
339
340# rounding to P of x
341$x = Math::BigFloat->new(10); $x->{_p} = -2;
342ok ($x->bdiv(3),'3.33');
343
344# round in div with requested P
345$x = Math::BigFloat->new(10);
346ok ($x->bdiv(3,undef,-2),'3.33');
347
348# round in div with requested P greater than fallback
349$Math::BigFloat::div_scale = 5;
350$x = Math::BigFloat->new(10);
351ok ($x->bdiv(3,undef,-8),'3.33333333');
352$Math::BigFloat::div_scale = 40;
353
354$x = Math::BigFloat->new(10); $y = Math::BigFloat->new(3); $y->{_a} = 4;
355ok ($x->bdiv($y),'3.333');
356ok ($x->{_a},4); ok ($y->{_a},4); # set's it since no fallback
357ok_undef ($x->{_p}); ok_undef ($y->{_p});
358
359# rounding to P of y
360$x = Math::BigFloat->new(10); $y = Math::BigFloat->new(3); $y->{_p} = -2;
361ok ($x->bdiv($y),'3.33');
362ok ($x->{_p},-2);
363 ok ($y->{_p},-2);
364ok_undef ($x->{_a}); ok_undef ($y->{_a});
365
366###############################################################################
367# test whether bround(-n) fails in MBF (undocumented in MBI)
368eval { $x = Math::BigFloat->new(1); $x->bround(-2); };
369ok ($@ =~ /^bround\(\) needs positive accuracy/,1);
370
371# test whether rounding to higher accuracy is no-op
372$x = Math::BigFloat->new(1); $x->{_a} = 4;
373ok ($x,'1.000');
374$x->bround(6); # must be no-op
375ok ($x->{_a},4);
376ok ($x,'1.000');
377
378$x = Math::BigInt->new(1230); $x->{_a} = 3;
379ok ($x,'1230');
380$x->bround(6); # must be no-op
381ok ($x->{_a},3);
382ok ($x,'1230');
383
384# bround(n) should set _a
385$x->bround(2); # smaller works
386ok ($x,'1200');
387ok ($x->{_a},2);
388
389# bround(-n) is undocumented and only used by MBF
390# bround(-n) should set _a
391$x = Math::BigInt->new(12345);
392$x->bround(-1);
393ok ($x,'12300');
394ok ($x->{_a},4);
395
396# bround(-n) should set _a
397$x = Math::BigInt->new(12345);
398$x->bround(-2);
399ok ($x,'12000');
400ok ($x->{_a},3);
401
402# bround(-n) should set _a
403$x = Math::BigInt->new(12345); $x->{_a} = 5;
404$x->bround(-3);
405ok ($x,'10000');
406ok ($x->{_a},2);
407
408# bround(-n) should set _a
409$x = Math::BigInt->new(12345); $x->{_a} = 5;
410$x->bround(-4);
411ok ($x,'00000');
412ok ($x->{_a},1);
413
414# bround(-n) should be noop if n too big
415$x = Math::BigInt->new(12345);
416$x->bround(-5);
417ok ($x,'0'); # scale to "big" => 0
418ok ($x->{_a},0);
419
420# bround(-n) should be noop if n too big
421$x = Math::BigInt->new(54321);
422$x->bround(-5);
423ok ($x,'100000'); # used by MBF to round 0.0054321 at 0.0_6_00000
424ok ($x->{_a},0);
425
426# bround(-n) should be noop if n too big
427$x = Math::BigInt->new(54321); $x->{_a} = 5;
428$x->bround(-6);
429ok ($x,'100000'); # no-op
430ok ($x->{_a},0);
431
432# bround(n) should set _a
433$x = Math::BigInt->new(12345); $x->{_a} = 5;
434$x->bround(5); # must be no-op
435ok ($x,'12345');
436ok ($x->{_a},5);
437
438# bround(n) should set _a
439$x = Math::BigInt->new(12345); $x->{_a} = 5;
440$x->bround(6); # must be no-op
441ok ($x,'12345');
442
e745a66c 443$x = Math::BigFloat->new('0.0061'); $x->bfround(-2);
444ok ($x,'0.01');
ee15d750 445
446###############################################################################
447# rounding with already set precision/accuracy
448
449$x = Math::BigFloat->new(1); $x->{_p} = -5;
450ok ($x,'1.00000');
451
452# further rounding donw
453ok ($x->bfround(-2),'1.00');
454ok ($x->{_p},-2);
455
456$x = Math::BigFloat->new(12345); $x->{_a} = 5;
457ok ($x->bround(2),'12000');
458ok ($x->{_a},2);
459
dccbb853 460$x = Math::BigFloat->new('1.2345'); $x->{_a} = 5;
ee15d750 461ok ($x->bround(2),'1.2');
462ok ($x->{_a},2);
463
464# mantissa/exponent format and A/P
dccbb853 465$x = Math::BigFloat->new('12345.678'); $x->accuracy(4);
ee15d750 466ok ($x,'12350'); ok ($x->{_a},4); ok_undef ($x->{_p});
467ok ($x->{_m}->{_f},1); ok ($x->{_e}->{_f},1);
468ok_undef ($x->{_m}->{_a}); ok_undef ($x->{_e}->{_a});
469ok_undef ($x->{_m}->{_p}); ok_undef ($x->{_e}->{_p});
470
471# check for no A/P in case of fallback
472# result
473$x = Math::BigFloat->new(100) / 3;
474ok_undef ($x->{_a}); ok_undef ($x->{_p});
475
476# result & reminder
477$x = Math::BigFloat->new(100) / 3; ($x,$y) = $x->bdiv(3);
478ok_undef ($x->{_a}); ok_undef ($x->{_p});
479ok_undef ($y->{_a}); ok_undef ($y->{_p});
480
481###############################################################################
482# math with two numbers with differen A and P
483
484$x = Math::BigFloat->new(12345); $x->accuracy(4); # '12340'
485$y = Math::BigFloat->new(12345); $y->accuracy(2); # '12000'
486ok ($x+$y,24000); # 12340+12000=> 24340 => 24000
487
488$x = Math::BigFloat->new(54321); $x->accuracy(4); # '12340'
489$y = Math::BigFloat->new(12345); $y->accuracy(3); # '12000'
490ok ($x-$y,42000); # 54320+12300=> 42020 => 42000
491
dccbb853 492$x = Math::BigFloat->new('1.2345'); $x->precision(-2); # '1.23'
493$y = Math::BigFloat->new('1.2345'); $y->precision(-4); # '1.2345'
494ok ($x+$y,'2.46'); # 1.2345+1.2300=> 2.4645 => 2.46
ee15d750 495
496###############################################################################
497# round should find and use proper class
498
499$x = Foo->new();
500ok ($x->round($Foo::accuracy),'a' x $Foo::accuracy);
501ok ($x->round(undef,$Foo::precision),'p' x $Foo::precision);
502ok ($x->bfround($Foo::precision),'p' x $Foo::precision);
503ok ($x->bround($Foo::accuracy),'a' x $Foo::accuracy);
504
505###############################################################################
506# find out whether _find_round_parameters is doing what's it's supposed to do
507
508$Math::BigInt::accuracy = undef;
509$Math::BigInt::precision = undef;
510$Math::BigInt::div_scale = 40;
511$Math::BigInt::round_mode = 'odd';
512
513$x = Math::BigInt->new(123);
514my @params = $x->_find_round_parameters();
515ok (scalar @params,1); # nothing to round
516
517@params = $x->_find_round_parameters(1);
518ok (scalar @params,4); # a=1
519ok ($params[0],$x); # self
520ok ($params[1],1); # a
521ok_undef ($params[2]); # p
522ok ($params[3],'odd'); # round_mode
523
524@params = $x->_find_round_parameters(undef,2);
525ok (scalar @params,4); # p=2
526ok ($params[0],$x); # self
527ok_undef ($params[1]); # a
528ok ($params[2],2); # p
529ok ($params[3],'odd'); # round_mode
530
531eval { @params = $x->_find_round_parameters(undef,2,'foo'); };
532ok ($@ =~ /^Unknown round mode 'foo'/,1);
533
534@params = $x->_find_round_parameters(undef,2,'+inf');
535ok (scalar @params,4); # p=2
536ok ($params[0],$x); # self
537ok_undef ($params[1]); # a
538ok ($params[2],2); # p
539ok ($params[3],'+inf'); # round_mode
540
541@params = $x->_find_round_parameters(2,-2,'+inf');
542ok (scalar @params,4); # p=2
543ok ($params[0],$x); # self
544ok ($params[1],2); # a
545ok ($params[2],-2); # p
546ok ($params[3],'+inf'); # round_mode
547
58cde26e 548# all done
549
550###############################################################################
551# Perl 5.005 does not like ok ($x,undef)
552
553sub ok_undef
554 {
555 my $x = shift;
556
557 ok (1,1) and return if !defined $x;
558 ok ($x,'undef');
559 }
560