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