3 # test rounding, accuracy, precicion and fallback, round_mode and mixing
12 # chdir 't' if -d 't';
13 unshift @INC, '../lib'; # for running manually
17 # for finding out whether round finds correct class
21 use vars qw/@ISA $precision $accuracy $div_scale $round_mode/;
22 @ISA = qw/Math::BigInt/;
32 my $self = { _a => undef, _p => undef, value => 5 };
40 return "$self->{value}";
43 # these will be called with the rounding precision or accuracy, depending on
47 my ($self,$a,$r) = @_;
48 $self->{value} = 'a' x $a;
60 my ($self,$p,$r) = @_;
61 $self->{value} = 'p' x $p;
72 ###############################################################################
73 # test defaults and set/get
75 ok_undef ($Math::BigInt::accuracy);
76 ok_undef ($Math::BigInt::precision);
77 ok_undef (Math::BigInt->accuracy());
78 ok_undef (Math::BigInt->precision());
79 ok ($Math::BigInt::div_scale,40);
80 ok (Math::BigInt::div_scale(),40);
81 ok ($Math::BigInt::round_mode,'even');
82 ok (Math::BigInt::round_mode(),'even');
84 ok_undef ($Math::BigFloat::accuracy);
85 ok_undef ($Math::BigFloat::precision);
86 ok_undef (Math::BigFloat->accuracy());
87 ok_undef (Math::BigFloat->precision());
88 ok ($Math::BigFloat::div_scale,40);
89 ok (Math::BigFloat::div_scale(),40);
90 ok ($Math::BigFloat::round_mode,'even');
91 ok (Math::BigFloat::round_mode(),'even');
94 foreach my $class (qw/Math::BigInt Math::BigFloat/)
96 ok_undef ($class->accuracy());
97 ok_undef ($class->precision());
98 ok ($class->round_mode(),'even');
99 ok ($class->div_scale(),40);
101 ok ($class->div_scale(20),20);
102 $class->div_scale(40); ok ($class->div_scale(),40);
104 ok ($class->round_mode('odd'),'odd');
105 $class->round_mode('even'); ok ($class->round_mode(),'even');
107 ok ($class->accuracy(2),2);
108 $class->accuracy(3); ok ($class->accuracy(),3);
109 ok_undef ($class->accuracy(undef));
111 ok ($class->precision(2),2);
112 ok ($class->precision(-2),-2);
113 $class->precision(3); ok ($class->precision(),3);
114 ok_undef ($class->precision(undef));
118 foreach (qw/5 42 -1 0/)
120 ok ($Math::BigFloat::accuracy = $_,$_);
121 ok ($Math::BigInt::accuracy = $_,$_);
123 ok_undef ($Math::BigFloat::accuracy = undef);
124 ok_undef ($Math::BigInt::accuracy = undef);
127 foreach (qw/5 42 -1 0/)
129 ok ($Math::BigFloat::precision = $_,$_);
130 ok ($Math::BigInt::precision = $_,$_);
132 ok_undef ($Math::BigFloat::precision = undef);
133 ok_undef ($Math::BigInt::precision = undef);
138 ok ($Math::BigFloat::div_scale = $_,$_);
139 ok ($Math::BigInt::div_scale = $_,$_);
141 # illegal values are possible for fallback due to no accessor
144 foreach (qw/odd even zero trunc +inf -inf/)
146 ok ($Math::BigFloat::round_mode = $_,$_);
147 ok ($Math::BigInt::round_mode = $_,$_);
149 $Math::BigFloat::round_mode = 'zero';
150 ok ($Math::BigFloat::round_mode,'zero');
151 ok ($Math::BigInt::round_mode,'-inf'); # from above
153 $Math::BigInt::accuracy = undef;
154 $Math::BigInt::precision = undef;
156 $x = Math::BigFloat->new(123.456);
157 ok_undef ($x->accuracy());
158 ok ($x->accuracy(5),5);
159 ok_undef ($x->accuracy(undef),undef);
160 ok_undef ($x->precision());
161 ok ($x->precision(5),5);
162 ok_undef ($x->precision(undef),undef);
164 # see if MBF changes MBIs values
165 ok ($Math::BigInt::accuracy = 42,42);
166 ok ($Math::BigFloat::accuracy = 64,64);
167 ok ($Math::BigInt::accuracy,42); # should be still 42
168 ok ($Math::BigFloat::accuracy,64); # should be still 64
170 ###############################################################################
171 # see if creating a number under set A or P will round it
173 $Math::BigInt::accuracy = 4;
174 $Math::BigInt::precision = 3;
176 ok (Math::BigInt->new(123456),123500); # with A
177 $Math::BigInt::accuracy = undef;
178 ok (Math::BigInt->new(123456),123000); # with P
180 $Math::BigFloat::accuracy = 4;
181 $Math::BigFloat::precision = -1;
182 $Math::BigInt::precision = undef;
184 ok (Math::BigFloat->new(123.456),123.5); # with A
185 $Math::BigFloat::accuracy = undef;
186 ok (Math::BigFloat->new(123.456),123.5); # with P from MBF, not MBI!
188 $Math::BigFloat::precision = undef;
190 ###############################################################################
191 # see if setting accuracy/precision actually rounds the number
193 $x = Math::BigFloat->new(123.456); $x->accuracy(4); ok ($x,123.5);
194 $x = Math::BigFloat->new(123.456); $x->precision(-2); ok ($x,123.46);
196 $x = Math::BigInt->new(123456); $x->accuracy(4); ok ($x,123500);
197 $x = Math::BigInt->new(123456); $x->precision(2); ok ($x,123500);
199 ###############################################################################
200 # test actual rounding via round()
202 $x = Math::BigFloat->new(123.456);
203 ok ($x->copy()->round(5,2),123.46);
204 ok ($x->copy()->round(4,2),123.5);
205 ok ($x->copy()->round(undef,-2),123.46);
206 ok ($x->copy()->round(undef,2),100);
208 $x = Math::BigFloat->new(123.45000);
209 ok ($x->copy()->round(undef,-1,'odd'),123.5);
211 # see if rounding is 'sticky'
212 $x = Math::BigFloat->new(123.4567);
213 $y = $x->copy()->bround(); # no-op since nowhere A or P defined
216 $y = $x->copy()->round(5,2);
217 ok ($y->accuracy(),5);
218 ok_undef ($y->precision()); # A has precedence, so P still unset
219 $y = $x->copy()->round(undef,2);
220 ok ($y->precision(),2);
221 ok_undef ($y->accuracy()); # P has precedence, so A still unset
223 # see if setting A clears P and vice versa
224 $x = Math::BigFloat->new(123.4567);
226 ok ($x->accuracy(4),4);
227 ok ($x->precision(-2),-2); # clear A
228 ok_undef ($x->accuracy());
230 $x = Math::BigFloat->new(123.4567);
232 ok ($x->precision(-2),-2);
233 ok ($x->accuracy(4),4); # clear P
234 ok_undef ($x->precision());
237 $x = Math::BigFloat->new(123.456); $x->accuracy(4); $x->precision(2);
238 $z = $x->copy(); ok_undef ($z->accuracy(),undef); ok ($z->precision(),2);
240 ###############################################################################
241 # test wether operations round properly afterwards
242 # These tests are not complete, since they do not excercise every "return"
243 # statement in the op's. But heh, it's better than nothing...
245 $x = Math::BigFloat->new(123.456);
246 $y = Math::BigFloat->new(654.321);
247 $x->{_a} = 5; # $x->accuracy(5) would round $x straightaway
248 $y->{_a} = 4; # $y->accuracy(4) would round $x straightaway
250 $z = $x + $y; ok ($z,777.8);
251 $z = $y - $x; ok ($z,530.9);
252 $z = $y * $x; ok ($z,80780);
253 $z = $x ** 2; ok ($z,15241);
254 $z = $x * $x; ok ($z,15241);
256 # not: $z = -$x; ok ($z,-123.46); ok ($x,123.456);
257 $z = $x->copy(); $z->{_a} = 2; $z = $z / 2; ok ($z,62);
258 $x = Math::BigFloat->new(123456); $x->{_a} = 4;
259 $z = $x->copy; $z++; ok ($z,123500);
261 $x = Math::BigInt->new(123456);
262 $y = Math::BigInt->new(654321);
263 $x->{_a} = 5; # $x->accuracy(5) would round $x straightaway
264 $y->{_a} = 4; # $y->accuracy(4) would round $x straightaway
266 $z = $x + $y; ok ($z,777800);
267 $z = $y - $x; ok ($z,530900);
268 $z = $y * $x; ok ($z,80780000000);
269 $z = $x ** 2; ok ($z,15241000000);
270 # not yet: $z = -$x; ok ($z,-123460); ok ($x,123456);
271 $z = $x->copy; $z++; ok ($z,123460);
272 $z = $x->copy(); $z->{_a} = 2; $z = $z / 2; ok ($z,62000);
274 $x = Math::BigInt->new(123400); $x->{_a} = 4;
275 ok ($x->bnot(),-123400); # not -1234001
277 # both babs() and bneg() don't need to round, since the input will already
278 # be rounded (either as $x or via new($string)), and they don't change the
280 # The two tests below peek at this by using _a illegally
281 $x = Math::BigInt->new(-123401); $x->{_a} = 4;
282 ok ($x->babs(),123401);
283 $x = Math::BigInt->new(-123401); $x->{_a} = 4;
284 ok ($x->bneg(),123401);
286 ###############################################################################
287 # test mixed arguments
289 $x = Math::BigFloat->new(10);
290 $u = Math::BigFloat->new(2.5);
291 $y = Math::BigInt->new(2);
293 $z = $x + $y; ok ($z,12); ok (ref($z),'Math::BigFloat');
294 $z = $x / $y; ok ($z,5); ok (ref($z),'Math::BigFloat');
295 $z = $u * $y; ok ($z,5); ok (ref($z),'Math::BigFloat');
297 $y = Math::BigInt->new(12345);
298 $z = $u->copy()->bmul($y,2,0,'odd'); ok ($z,31000);
299 $z = $u->copy()->bmul($y,3,0,'odd'); ok ($z,30900);
300 $z = $u->copy()->bmul($y,undef,0,'odd'); ok ($z,30863);
301 $z = $u->copy()->bmul($y,undef,1,'odd'); ok ($z,30860);
302 $z = $u->copy()->bmul($y,undef,-1,'odd'); ok ($z,30862.5);
305 # $z = $y->copy()->bmul($u,2,0,'odd'); ok ($z,31000);
306 # $z = $y * $u; ok ($z,5); ok (ref($z),'Math::BigInt');
307 # $z = $y + $x; ok ($z,12); ok (ref($z),'Math::BigInt');
308 # $z = $y / $x; ok ($z,0); ok (ref($z),'Math::BigInt');
310 ###############################################################################
311 # rounding in bdiv with fallback and already set A or P
313 $Math::BigFloat::accuracy = undef;
314 $Math::BigFloat::precision = undef;
315 $Math::BigFloat::div_scale = 40;
317 $x = Math::BigFloat->new(10); $x->{_a} = 4;
318 ok ($x->bdiv(3),'3.333');
319 ok ($x->{_a},4); # set's it since no fallback
321 $x = Math::BigFloat->new(10); $x->{_a} = 4; $y = Math::BigFloat->new(3);
322 ok ($x->bdiv($y),'3.333');
323 ok ($x->{_a},4); # set's it since no fallback
326 $x = Math::BigFloat->new(10); $x->{_p} = -2;
327 ok ($x->bdiv(3),'3.33');
329 # round in div with requested P
330 $x = Math::BigFloat->new(10);
331 ok ($x->bdiv(3,undef,-2),'3.33');
333 # round in div with requested P greater than fallback
334 $Math::BigFloat::div_scale = 5;
335 $x = Math::BigFloat->new(10);
336 ok ($x->bdiv(3,undef,-8),'3.33333333');
337 $Math::BigFloat::div_scale = 40;
339 $x = Math::BigFloat->new(10); $y = Math::BigFloat->new(3); $y->{_a} = 4;
340 ok ($x->bdiv($y),'3.333');
341 ok ($x->{_a},4); ok ($y->{_a},4); # set's it since no fallback
342 ok_undef ($x->{_p}); ok_undef ($y->{_p});
345 $x = Math::BigFloat->new(10); $y = Math::BigFloat->new(3); $y->{_p} = -2;
346 ok ($x->bdiv($y),'3.33');
349 ok_undef ($x->{_a}); ok_undef ($y->{_a});
351 ###############################################################################
352 # test whether bround(-n) fails in MBF (undocumented in MBI)
353 eval { $x = Math::BigFloat->new(1); $x->bround(-2); };
354 ok ($@ =~ /^bround\(\) needs positive accuracy/,1);
356 # test whether rounding to higher accuracy is no-op
357 $x = Math::BigFloat->new(1); $x->{_a} = 4;
359 $x->bround(6); # must be no-op
363 $x = Math::BigInt->new(1230); $x->{_a} = 3;
365 $x->bround(6); # must be no-op
369 # bround(n) should set _a
370 $x->bround(2); # smaller works
374 # bround(-n) is undocumented and only used by MBF
375 # bround(-n) should set _a
376 $x = Math::BigInt->new(12345);
381 # bround(-n) should set _a
382 $x = Math::BigInt->new(12345);
387 # bround(-n) should set _a
388 $x = Math::BigInt->new(12345); $x->{_a} = 5;
393 # bround(-n) should set _a
394 $x = Math::BigInt->new(12345); $x->{_a} = 5;
399 # bround(-n) should be noop if n too big
400 $x = Math::BigInt->new(12345);
402 ok ($x,'0'); # scale to "big" => 0
405 # bround(-n) should be noop if n too big
406 $x = Math::BigInt->new(54321);
408 ok ($x,'100000'); # used by MBF to round 0.0054321 at 0.0_6_00000
411 # bround(-n) should be noop if n too big
412 $x = Math::BigInt->new(54321); $x->{_a} = 5;
414 ok ($x,'100000'); # no-op
417 # bround(n) should set _a
418 $x = Math::BigInt->new(12345); $x->{_a} = 5;
419 $x->bround(5); # must be no-op
423 # bround(n) should set _a
424 $x = Math::BigInt->new(12345); $x->{_a} = 5;
425 $x->bround(6); # must be no-op
428 $x = Math::BigFloat->new(0.0061); $x->bfround(-2);
431 ###############################################################################
432 # rounding with already set precision/accuracy
434 $x = Math::BigFloat->new(1); $x->{_p} = -5;
437 # further rounding donw
438 ok ($x->bfround(-2),'1.00');
441 $x = Math::BigFloat->new(12345); $x->{_a} = 5;
442 ok ($x->bround(2),'12000');
445 $x = Math::BigFloat->new(1.2345); $x->{_a} = 5;
446 ok ($x->bround(2),'1.2');
449 # mantissa/exponent format and A/P
450 $x = Math::BigFloat->new(12345.678); $x->accuracy(4);
451 ok ($x,'12350'); ok ($x->{_a},4); ok_undef ($x->{_p});
452 ok ($x->{_m}->{_f},1); ok ($x->{_e}->{_f},1);
453 ok_undef ($x->{_m}->{_a}); ok_undef ($x->{_e}->{_a});
454 ok_undef ($x->{_m}->{_p}); ok_undef ($x->{_e}->{_p});
456 # check for no A/P in case of fallback
458 $x = Math::BigFloat->new(100) / 3;
459 ok_undef ($x->{_a}); ok_undef ($x->{_p});
462 $x = Math::BigFloat->new(100) / 3; ($x,$y) = $x->bdiv(3);
463 ok_undef ($x->{_a}); ok_undef ($x->{_p});
464 ok_undef ($y->{_a}); ok_undef ($y->{_p});
466 ###############################################################################
467 # math with two numbers with differen A and P
469 $x = Math::BigFloat->new(12345); $x->accuracy(4); # '12340'
470 $y = Math::BigFloat->new(12345); $y->accuracy(2); # '12000'
471 ok ($x+$y,24000); # 12340+12000=> 24340 => 24000
473 $x = Math::BigFloat->new(54321); $x->accuracy(4); # '12340'
474 $y = Math::BigFloat->new(12345); $y->accuracy(3); # '12000'
475 ok ($x-$y,42000); # 54320+12300=> 42020 => 42000
477 $x = Math::BigFloat->new(1.2345); $x->precision(-2); # '1.23'
478 $y = Math::BigFloat->new(1.2345); $y->precision(-4); # '1.2345'
479 ok ($x+$y,2.46); # 1.2345+1.2300=> 2.4645 => 2.46
481 ###############################################################################
482 # round should find and use proper class
485 ok ($x->round($Foo::accuracy),'a' x $Foo::accuracy);
486 ok ($x->round(undef,$Foo::precision),'p' x $Foo::precision);
487 ok ($x->bfround($Foo::precision),'p' x $Foo::precision);
488 ok ($x->bround($Foo::accuracy),'a' x $Foo::accuracy);
490 ###############################################################################
491 # find out whether _find_round_parameters is doing what's it's supposed to do
493 $Math::BigInt::accuracy = undef;
494 $Math::BigInt::precision = undef;
495 $Math::BigInt::div_scale = 40;
496 $Math::BigInt::round_mode = 'odd';
498 $x = Math::BigInt->new(123);
499 my @params = $x->_find_round_parameters();
500 ok (scalar @params,1); # nothing to round
502 @params = $x->_find_round_parameters(1);
503 ok (scalar @params,4); # a=1
504 ok ($params[0],$x); # self
505 ok ($params[1],1); # a
506 ok_undef ($params[2]); # p
507 ok ($params[3],'odd'); # round_mode
509 @params = $x->_find_round_parameters(undef,2);
510 ok (scalar @params,4); # p=2
511 ok ($params[0],$x); # self
512 ok_undef ($params[1]); # a
513 ok ($params[2],2); # p
514 ok ($params[3],'odd'); # round_mode
516 eval { @params = $x->_find_round_parameters(undef,2,'foo'); };
517 ok ($@ =~ /^Unknown round mode 'foo'/,1);
519 @params = $x->_find_round_parameters(undef,2,'+inf');
520 ok (scalar @params,4); # p=2
521 ok ($params[0],$x); # self
522 ok_undef ($params[1]); # a
523 ok ($params[2],2); # p
524 ok ($params[3],'+inf'); # round_mode
526 @params = $x->_find_round_parameters(2,-2,'+inf');
527 ok (scalar @params,4); # p=2
528 ok ($params[0],$x); # self
529 ok ($params[1],2); # a
530 ok ($params[2],-2); # p
531 ok ($params[3],'+inf'); # round_mode
535 ###############################################################################
536 # Perl 5.005 does not like ok ($x,undef)
542 ok (1,1) and return if !defined $x;