3 # test rounding, accuracy, precicion and fallback, round_mode and mixing
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.
16 unshift @INC, '../lib'; # for running manually
20 # for finding out whether round finds correct class
24 use vars qw/@ISA $precision $accuracy $div_scale $round_mode/;
25 @ISA = qw/Math::BigInt/;
35 my $self = { _a => undef, _p => undef, value => 5 };
43 return "$self->{value}";
46 # these will be called with the rounding precision or accuracy, depending on
50 my ($self,$a,$r) = @_;
51 $self->{value} = 'a' x $a;
63 my ($self,$p,$r) = @_;
64 $self->{value} = 'p' x $p;
75 ###############################################################################
76 # test defaults and set/get
78 ok_undef ($Math::BigInt::accuracy);
79 ok_undef ($Math::BigInt::precision);
80 ok_undef (Math::BigInt::accuracy());
81 ok_undef (Math::BigInt::precision());
82 ok_undef (Math::BigInt->accuracy());
83 ok_undef (Math::BigInt->precision());
84 ok ($Math::BigInt::div_scale,40);
85 ok (Math::BigInt::div_scale(),40);
86 ok ($Math::BigInt::round_mode,'even');
87 ok (Math::BigInt::round_mode(),'even');
88 ok (Math::BigInt->round_mode(),'even');
90 ok_undef ($Math::BigFloat::accuracy);
91 ok_undef ($Math::BigFloat::precision);
92 ok_undef (Math::BigFloat::accuracy());
93 ok_undef (Math::BigFloat::accuracy());
94 ok_undef (Math::BigFloat->precision());
95 ok_undef (Math::BigFloat->precision());
96 ok ($Math::BigFloat::div_scale,40);
97 ok (Math::BigFloat::div_scale(),40);
98 ok ($Math::BigFloat::round_mode,'even');
99 ok (Math::BigFloat::round_mode(),'even');
100 ok (Math::BigFloat->round_mode(),'even');
103 ok ($Math::BigInt::rnd_mode,'even');
104 ok ($Math::BigFloat::rnd_mode,'even');
106 $x = eval 'Math::BigInt->round_mode("huhmbi");';
107 ok ($@ =~ /^Unknown round mode huhmbi at/);
109 $x = eval 'Math::BigFloat->round_mode("huhmbf");';
110 ok ($@ =~ /^Unknown round mode huhmbf at/);
112 # old way (now with test for validity)
113 $x = eval '$Math::BigInt::rnd_mode = "huhmbi";';
114 ok ($@ =~ /^Unknown round mode huhmbi at/);
115 $x = eval '$Math::BigFloat::rnd_mode = "huhmbi";';
116 ok ($@ =~ /^Unknown round mode huhmbi at/);
117 # see if accessor also changes old variable
118 Math::BigInt->round_mode('odd');
119 ok ($Math::BigInt::rnd_mode,'odd');
120 Math::BigFloat->round_mode('odd');
121 ok ($Math::BigFloat::rnd_mode,'odd');
123 Math::BigInt->round_mode('even');
124 Math::BigFloat->round_mode('even');
127 foreach my $class (qw/Math::BigInt Math::BigFloat/)
129 ok_undef ($class->accuracy());
130 ok_undef ($class->precision());
131 ok ($class->round_mode(),'even');
132 ok ($class->div_scale(),40);
134 ok ($class->div_scale(20),20);
135 $class->div_scale(40); ok ($class->div_scale(),40);
137 ok ($class->round_mode('odd'),'odd');
138 $class->round_mode('even'); ok ($class->round_mode(),'even');
140 ok ($class->accuracy(2),2);
141 $class->accuracy(3); ok ($class->accuracy(),3);
142 ok_undef ($class->accuracy(undef));
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));
151 foreach (qw/5 42 -1 0/)
153 ok ($Math::BigFloat::accuracy = $_,$_);
154 ok ($Math::BigInt::accuracy = $_,$_);
156 ok_undef ($Math::BigFloat::accuracy = undef);
157 ok_undef ($Math::BigInt::accuracy = undef);
160 foreach (qw/5 42 -1 0/)
162 ok ($Math::BigFloat::precision = $_,$_);
163 ok ($Math::BigInt::precision = $_,$_);
165 ok_undef ($Math::BigFloat::precision = undef);
166 ok_undef ($Math::BigInt::precision = undef);
171 ok ($Math::BigFloat::div_scale = $_,$_);
172 ok ($Math::BigInt::div_scale = $_,$_);
174 # illegal values are possible for fallback due to no accessor
177 foreach (qw/odd even zero trunc +inf -inf/)
179 ok ($Math::BigFloat::round_mode = $_,$_);
180 ok ($Math::BigInt::round_mode = $_,$_);
182 $Math::BigFloat::round_mode = 'zero';
183 ok ($Math::BigFloat::round_mode,'zero');
184 ok ($Math::BigInt::round_mode,'-inf'); # from above
186 $Math::BigInt::accuracy = undef;
187 $Math::BigInt::precision = undef;
189 $x = Math::BigFloat->new('123.456');
190 ok_undef ($x->accuracy());
191 ok ($x->accuracy(5),5);
192 ok_undef ($x->accuracy(undef),undef);
193 ok_undef ($x->precision());
194 ok ($x->precision(5),5);
195 ok_undef ($x->precision(undef),undef);
197 # see if MBF changes MBIs values
198 ok ($Math::BigInt::accuracy = 42,42);
199 ok ($Math::BigFloat::accuracy = 64,64);
200 ok ($Math::BigInt::accuracy,42); # should be still 42
201 ok ($Math::BigFloat::accuracy,64); # should be still 64
203 ###############################################################################
204 # see if creating a number under set A or P will round it
206 $Math::BigInt::accuracy = 4;
207 $Math::BigInt::precision = 3;
209 ok (Math::BigInt->new(123456),123500); # with A
210 $Math::BigInt::accuracy = undef;
211 ok (Math::BigInt->new(123456),123000); # with P
213 $Math::BigFloat::accuracy = 4;
214 $Math::BigFloat::precision = -1;
215 $Math::BigInt::precision = undef;
217 ok (Math::BigFloat->new('123.456'),'123.5'); # with A
218 $Math::BigFloat::accuracy = undef;
219 ok (Math::BigFloat->new('123.456'),'123.5'); # with P from MBF, not MBI!
221 $Math::BigFloat::precision = undef;
223 ###############################################################################
224 # see if setting accuracy/precision actually rounds the number
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');
229 $x = Math::BigInt->new(123456); $x->accuracy(4); ok ($x,123500);
230 $x = Math::BigInt->new(123456); $x->precision(2); ok ($x,123500);
232 ###############################################################################
233 # test actual rounding via round()
235 $x = Math::BigFloat->new('123.456');
236 ok ($x->copy()->round(5,2),'123.46');
237 ok ($x->copy()->round(4,2),'123.5');
238 ok ($x->copy()->round(undef,-2),'123.46');
239 ok ($x->copy()->round(undef,2),100);
241 $x = Math::BigFloat->new('123.45000');
242 ok ($x->copy()->round(undef,-1,'odd'),'123.5');
244 # see if rounding is 'sticky'
245 $x = Math::BigFloat->new('123.4567');
246 $y = $x->copy()->bround(); # no-op since nowhere A or P defined
249 $y = $x->copy()->round(5,2);
250 ok ($y->accuracy(),5);
251 ok_undef ($y->precision()); # A has precedence, so P still unset
252 $y = $x->copy()->round(undef,2);
253 ok ($y->precision(),2);
254 ok_undef ($y->accuracy()); # P has precedence, so A still unset
256 # see if setting A clears P and vice versa
257 $x = Math::BigFloat->new('123.4567');
259 ok ($x->accuracy(4),4);
260 ok ($x->precision(-2),-2); # clear A
261 ok_undef ($x->accuracy());
263 $x = Math::BigFloat->new('123.4567');
265 ok ($x->precision(-2),-2);
266 ok ($x->accuracy(4),4); # clear P
267 ok_undef ($x->precision());
270 $x = Math::BigFloat->new(123.456); $x->accuracy(4); $x->precision(2);
271 $z = $x->copy(); ok_undef ($z->accuracy(),undef); ok ($z->precision(),2);
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...
278 $x = Math::BigFloat->new('123.456');
279 $y = Math::BigFloat->new('654.321');
280 $x->{_a} = 5; # $x->accuracy(5) would round $x straightaway
281 $y->{_a} = 4; # $y->accuracy(4) would round $x straightaway
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');
289 # not: $z = -$x; ok ($z,'-123.46'); ok ($x,'123.456');
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);
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
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);
307 $x = Math::BigInt->new(123400); $x->{_a} = 4;
308 ok ($x->bnot(),-123400); # not -1234001
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
313 # The two tests below peek at this by using _a illegally
314 $x = Math::BigInt->new(-123401); $x->{_a} = 4;
315 ok ($x->babs(),123401);
316 $x = Math::BigInt->new(-123401); $x->{_a} = 4;
317 ok ($x->bneg(),123401);
319 ###############################################################################
320 # test mixed arguments
322 $x = Math::BigFloat->new(10);
323 $u = Math::BigFloat->new(2.5);
324 $y = Math::BigInt->new(2);
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');
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);
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');
343 ###############################################################################
344 # rounding in bdiv with fallback and already set A or P
346 $Math::BigFloat::accuracy = undef;
347 $Math::BigFloat::precision = undef;
348 $Math::BigFloat::div_scale = 40;
350 $x = Math::BigFloat->new(10); $x->{_a} = 4;
351 ok ($x->bdiv(3),'3.333');
352 ok ($x->{_a},4); # set's it since no fallback
354 $x = Math::BigFloat->new(10); $x->{_a} = 4; $y = Math::BigFloat->new(3);
355 ok ($x->bdiv($y),'3.333');
356 ok ($x->{_a},4); # set's it since no fallback
359 $x = Math::BigFloat->new(10); $x->{_p} = -2;
360 ok ($x->bdiv(3),'3.33');
362 # round in div with requested P
363 $x = Math::BigFloat->new(10);
364 ok ($x->bdiv(3,undef,-2),'3.33');
366 # round in div with requested P greater than fallback
367 $Math::BigFloat::div_scale = 5;
368 $x = Math::BigFloat->new(10);
369 ok ($x->bdiv(3,undef,-8),'3.33333333');
370 $Math::BigFloat::div_scale = 40;
372 $x = Math::BigFloat->new(10); $y = Math::BigFloat->new(3); $y->{_a} = 4;
373 ok ($x->bdiv($y),'3.333');
374 ok ($x->{_a},4); ok ($y->{_a},4); # set's it since no fallback
375 ok_undef ($x->{_p}); ok_undef ($y->{_p});
378 $x = Math::BigFloat->new(10); $y = Math::BigFloat->new(3); $y->{_p} = -2;
379 ok ($x->bdiv($y),'3.33');
382 ok_undef ($x->{_a}); ok_undef ($y->{_a});
384 ###############################################################################
385 # test whether bround(-n) fails in MBF (undocumented in MBI)
386 eval { $x = Math::BigFloat->new(1); $x->bround(-2); };
387 ok ($@ =~ /^bround\(\) needs positive accuracy/,1);
389 # test whether rounding to higher accuracy is no-op
390 $x = Math::BigFloat->new(1); $x->{_a} = 4;
392 $x->bround(6); # must be no-op
396 $x = Math::BigInt->new(1230); $x->{_a} = 3;
398 $x->bround(6); # must be no-op
402 # bround(n) should set _a
403 $x->bround(2); # smaller works
407 # bround(-n) is undocumented and only used by MBF
408 # bround(-n) should set _a
409 $x = Math::BigInt->new(12345);
414 # bround(-n) should set _a
415 $x = Math::BigInt->new(12345);
420 # bround(-n) should set _a
421 $x = Math::BigInt->new(12345); $x->{_a} = 5;
426 # bround(-n) should set _a
427 $x = Math::BigInt->new(12345); $x->{_a} = 5;
432 # bround(-n) should be noop if n too big
433 $x = Math::BigInt->new(12345);
435 ok ($x,'0'); # scale to "big" => 0
438 # bround(-n) should be noop if n too big
439 $x = Math::BigInt->new(54321);
441 ok ($x,'100000'); # used by MBF to round 0.0054321 at 0.0_6_00000
444 # bround(-n) should be noop if n too big
445 $x = Math::BigInt->new(54321); $x->{_a} = 5;
447 ok ($x,'100000'); # no-op
450 # bround(n) should set _a
451 $x = Math::BigInt->new(12345); $x->{_a} = 5;
452 $x->bround(5); # must be no-op
456 # bround(n) should set _a
457 $x = Math::BigInt->new(12345); $x->{_a} = 5;
458 $x->bround(6); # must be no-op
461 $x = Math::BigFloat->new('0.0061'); $x->bfround(-2);
464 ###############################################################################
465 # rounding with already set precision/accuracy
467 $x = Math::BigFloat->new(1); $x->{_p} = -5;
470 # further rounding donw
471 ok ($x->bfround(-2),'1.00');
474 $x = Math::BigFloat->new(12345); $x->{_a} = 5;
475 ok ($x->bround(2),'12000');
478 $x = Math::BigFloat->new('1.2345'); $x->{_a} = 5;
479 ok ($x->bround(2),'1.2');
482 # mantissa/exponent format and A/P
483 $x = Math::BigFloat->new('12345.678'); $x->accuracy(4);
484 ok ($x,'12350'); ok ($x->{_a},4); ok_undef ($x->{_p});
485 ok ($x->{_m}->{_f},1); ok ($x->{_e}->{_f},1);
486 ok_undef ($x->{_m}->{_a}); ok_undef ($x->{_e}->{_a});
487 ok_undef ($x->{_m}->{_p}); ok_undef ($x->{_e}->{_p});
489 # check for no A/P in case of fallback
491 $x = Math::BigFloat->new(100) / 3;
492 ok_undef ($x->{_a}); ok_undef ($x->{_p});
495 $x = Math::BigFloat->new(100) / 3; ($x,$y) = $x->bdiv(3);
496 ok_undef ($x->{_a}); ok_undef ($x->{_p});
497 ok_undef ($y->{_a}); ok_undef ($y->{_p});
499 ###############################################################################
500 # math with two numbers with differen A and P
502 $x = Math::BigFloat->new(12345); $x->accuracy(4); # '12340'
503 $y = Math::BigFloat->new(12345); $y->accuracy(2); # '12000'
504 ok ($x+$y,24000); # 12340+12000=> 24340 => 24000
506 $x = Math::BigFloat->new(54321); $x->accuracy(4); # '12340'
507 $y = Math::BigFloat->new(12345); $y->accuracy(3); # '12000'
508 ok ($x-$y,42000); # 54320+12300=> 42020 => 42000
510 $x = Math::BigFloat->new('1.2345'); $x->precision(-2); # '1.23'
511 $y = Math::BigFloat->new('1.2345'); $y->precision(-4); # '1.2345'
512 ok ($x+$y,'2.46'); # 1.2345+1.2300=> 2.4645 => 2.46
514 ###############################################################################
515 # round should find and use proper class
518 ok ($x->round($Foo::accuracy),'a' x $Foo::accuracy);
519 ok ($x->round(undef,$Foo::precision),'p' x $Foo::precision);
520 ok ($x->bfround($Foo::precision),'p' x $Foo::precision);
521 ok ($x->bround($Foo::accuracy),'a' x $Foo::accuracy);
523 ###############################################################################
524 # find out whether _find_round_parameters is doing what's it's supposed to do
526 $Math::BigInt::accuracy = undef;
527 $Math::BigInt::precision = undef;
528 $Math::BigInt::div_scale = 40;
529 $Math::BigInt::round_mode = 'odd';
531 $x = Math::BigInt->new(123);
532 my @params = $x->_find_round_parameters();
533 ok (scalar @params,1); # nothing to round
535 @params = $x->_find_round_parameters(1);
536 ok (scalar @params,4); # a=1
537 ok ($params[0],$x); # self
538 ok ($params[1],1); # a
539 ok_undef ($params[2]); # p
540 ok ($params[3],'odd'); # round_mode
542 @params = $x->_find_round_parameters(undef,2);
543 ok (scalar @params,4); # p=2
544 ok ($params[0],$x); # self
545 ok_undef ($params[1]); # a
546 ok ($params[2],2); # p
547 ok ($params[3],'odd'); # round_mode
549 eval { @params = $x->_find_round_parameters(undef,2,'foo'); };
550 ok ($@ =~ /^Unknown round mode 'foo'/,1);
552 @params = $x->_find_round_parameters(undef,2,'+inf');
553 ok (scalar @params,4); # p=2
554 ok ($params[0],$x); # self
555 ok_undef ($params[1]); # a
556 ok ($params[2],2); # p
557 ok ($params[3],'+inf'); # round_mode
559 @params = $x->_find_round_parameters(2,-2,'+inf');
560 ok (scalar @params,4); # p=2
561 ok ($params[0],$x); # self
562 ok ($params[1],2); # a
563 ok ($params[2],-2); # p
564 ok ($params[3],'+inf'); # round_mode
568 ###############################################################################
569 # Perl 5.005 does not like ok ($x,undef)
575 ok (1,1) and return if !defined $x;