Skip to content

Commit 2d41ecb

Browse files
committed
add a extra option to round for floor/ceil/round with precision
see Perl#9
1 parent 0ce55c5 commit 2d41ecb

File tree

3 files changed

+94
-15
lines changed

3 files changed

+94
-15
lines changed

Format.pm

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Number::Format - Perl extension for formatting numbers
2424
$number = $x->unformat_number($formatted);
2525
2626
use Number::Format qw(:subs);
27-
$formatted = round($number, $precision);
27+
$formatted = round($number, $precision, $roundoption);
2828
$formatted = format_number($number, $precision, $trailing_zeroes);
2929
$formatted = format_negative($number, $picture);
3030
$formatted = format_picture($number, $picture);
@@ -59,6 +59,7 @@ formatting engine. Valid parameters are:
5959
DECIMAL_DIGITS - number of digits to the right of dec point (def 2)
6060
DECIMAL_FILL - boolean; whether to add zeroes to fill out decimal
6161
NEG_FORMAT - format to display negative numbers (def ``-x'')
62+
ROUND_OPTION - decide to floor or normal rounding or ceil
6263
KILO_SUFFIX - suffix to add when format_bytes formats kilobytes (trad)
6364
MEGA_SUFFIX - " " " " " " megabytes (trad)
6465
GIGA_SUFFIX - " " " " " " gigabytes (trad)
@@ -100,6 +101,7 @@ the parameters are:
100101
DECIMAL_DIGITS = 2
101102
DECIMAL_FILL = 0
102103
NEG_FORMAT = '-x'
104+
ROUND_OPTION = 0
103105
KILO_SUFFIX = 'K'
104106
MEGA_SUFFIX = 'M'
105107
GIGA_SUFFIX = 'G'
@@ -127,6 +129,9 @@ positive representation of the number being passed to that function.
127129
C<format_number()> and C<format_price()> utilize this feature by
128130
calling C<format_negative()> if the number was less than 0.
129131
132+
C<ROUND_OPTION> is only used by C<round()>. A number less then 0 means
133+
floor, a number bigger then 0 ceil and a 0 stand for a normal rounding.
134+
130135
C<KILO_SUFFIX>, C<MEGA_SUFFIX>, and C<GIGA_SUFFIX> are used by
131136
C<format_bytes()> when the value is over 1024, 1024*1024, or
132137
1024*1024*1024, respectively. The default values are "K", "M", and
@@ -195,7 +200,7 @@ our @EXPORT_LC_MONETARY =
195200
$N_CS_PRECEDES $N_SEP_BY_SPACE $P_SIGN_POSN $N_SIGN_POSN );
196201

197202
our @EXPORT_OTHER =
198-
qw( $DECIMAL_DIGITS $DECIMAL_FILL $NEG_FORMAT
203+
qw( $DECIMAL_DIGITS $DECIMAL_FILL $NEG_FORMAT $ROUND_OPTION
199204
$KILO_SUFFIX $MEGA_SUFFIX $GIGA_SUFFIX
200205
$KIBI_SUFFIX $MEBI_SUFFIX $GIBI_SUFFIX );
201206

@@ -242,6 +247,7 @@ our $N_SIGN_POSN = 1; # sign rules for negative: 0-4
242247
our $DECIMAL_DIGITS = 2;
243248
our $DECIMAL_FILL = 0;
244249
our $NEG_FORMAT = '-x';
250+
our $ROUND_OPTION = 0;
245251
our $KILO_SUFFIX = 'K';
246252
our $MEGA_SUFFIX = 'M';
247253
our $GIGA_SUFFIX = 'G';
@@ -276,6 +282,7 @@ our $DEFAULT_LOCALE = { (
276282
decimal_digits => $DECIMAL_DIGITS,
277283
decimal_fill => $DECIMAL_FILL,
278284
neg_format => $NEG_FORMAT,
285+
round_option => $ROUND_OPTION,
279286
kilo_suffix => $KILO_SUFFIX,
280287
mega_suffix => $MEGA_SUFFIX,
281288
giga_suffix => $GIGA_SUFFIX,
@@ -482,18 +489,25 @@ sub new
482489

483490
##----------------------------------------------------------------------
484491

485-
=item round($number, $precision)
492+
=item round($number, $precision, $roundoption)
486493
487494
Rounds the number to the specified precision. If C<$precision> is
488495
omitted, the value of the C<DECIMAL_DIGITS> parameter is used (default
489496
value 2). Both input and output are numeric (the function uses math
490497
operators rather than string manipulation to do its job), The value of
491-
C<$precision> may be any integer, positive or negative. Examples:
492-
493-
round(3.14159) yields 3.14
494-
round(3.14159, 4) yields 3.1416
495-
round(42.00, 4) yields 42
496-
round(1234, -2) yields 1200
498+
C<$precision> may be any integer, positive or negative. If C<$roundoption>
499+
is omitted, the value of the C<ROUND_OPTION> paramter is used (default
500+
value 0). Examples:
501+
502+
round(3.14159) yields 3.14
503+
round(3.14159, undef, 1) yields 3.15
504+
round(3.14159, undef, -1) yields 3.14
505+
round(3.14159, 4) yields 3.1416
506+
round(42.00, 4) yields 42
507+
round(1234, -2) yields 1200
508+
round(1234, -2, 1) yields 1300
509+
round(1298, -2) yields 1300
510+
round(1298, -2, -1) yields 1200
497511
498512
Since this is a mathematical rather than string oriented function,
499513
there will be no trailing zeroes to the right of the decimal point,
@@ -505,24 +519,39 @@ variables, use C<format_number()> instead.
505519

506520
sub round
507521
{
508-
my ($self, $number, $precision) = _get_self @_;
522+
my ($self, $number, $precision, $roundoption) = _get_self @_;
509523

510524
unless (defined($number))
511525
{
512526
_complain_undef();
513527
$number = 0;
514528
}
515529

516-
$precision = $self->{decimal_digits} unless defined $precision;
517-
$precision = 2 unless defined $precision;
530+
$precision = $self->{decimal_digits} unless defined $precision;
531+
$precision = 2 unless defined $precision;
532+
$roundoption = $self->{round_option} unless defined $roundoption;
533+
$roundoption = 0 unless defined $roundoption;
518534

519535
croak("precision must be integer")
520536
unless int($precision) == $precision;
521537

522538
if (ref($number) && $number->isa("Math::BigFloat"))
523539
{
524540
my $rounded = $number->copy();
525-
$rounded->precision(-$precision);
541+
542+
if ($roundoption) {
543+
$rounded *= 10**$precision;
544+
if ($roundoption < 0) {
545+
$rounded->bfloor();
546+
}
547+
elsif ($roundoption > 0) {
548+
$rounded->bceil();
549+
}
550+
$rounded /= 10**$precision;
551+
}
552+
553+
$rounded->round(0, -$precision);
554+
526555
return $rounded;
527556
}
528557

@@ -536,8 +565,31 @@ sub round
536565

537566
# We need to add 1e-14 to avoid some rounding errors due to the
538567
# way floating point numbers work - see string-eq test in t/round.t
539-
$result = int($product + .5 + 1e-14) / $multiplier;
540-
$result = -$result if $sign < 0;
568+
if ($roundoption < 0) {
569+
if ($sign < 0) {
570+
$result = int($product + 1 - 1e-14) / -$multiplier;
571+
}
572+
else {
573+
$result = int($product) / $multiplier;
574+
}
575+
}
576+
elsif ($roundoption == 0) {
577+
if ($sign < 0) {
578+
$result = int($product + 0.5 + 1e-14) / -$multiplier;
579+
}
580+
else {
581+
$result = int($product + 0.5 + 1e-14) / $multiplier;
582+
}
583+
}
584+
else {
585+
if ($sign < 0) {
586+
$result = int($product) / -$multiplier;
587+
}
588+
else {
589+
$result = int($product + 1 - 1e-14) / $multiplier;
590+
}
591+
}
592+
541593
return $result;
542594
}
543595

t/bigfloat.t

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ SKIP:
1818

1919
my $nf = Number::Format->new();
2020
is($nf->round(Math::BigFloat->new(123.456), 2), '123.46', "round");
21+
is($nf->round(Math::BigFloat->new(1), 0, -1), '1', "floor");
22+
is($nf->round(Math::BigFloat->new(1), 0, 0), '1', "normal");
23+
is($nf->round(Math::BigFloat->new(1), 0, +1), '1', "ceil");
24+
is($nf->round(Math::BigFloat->new(1.1), 0, -1), '1', "floor");
25+
is($nf->round(Math::BigFloat->new(1.1), 0, 0), '1', "normal");
26+
is($nf->round(Math::BigFloat->new(1.1), 0, +1), '2', "ceil");
27+
is($nf->round(Math::BigFloat->new(1.15), 1, -1), '1.1', "floor");
28+
is($nf->round(Math::BigFloat->new(1.15), 1, 0), '1.2', "normal");
29+
is($nf->round(Math::BigFloat->new(1.15), 1, +1), '1.2', "ceil");
30+
is($nf->round(Math::BigFloat->new(-1.1), 0, -1), '-2', "floor");
31+
is($nf->round(Math::BigFloat->new(-1.1), 0, 0), '-1', "normal");
32+
is($nf->round(Math::BigFloat->new(-1.1), 0, +1), '-1', "ceil");
2133
is($nf->format_number(Math::BigFloat->new(500.27), 2, 1), '500.27');
2234
is($nf->format_number(Math::BigFloat->bpi(100), 7, 1), '3.1415927');
2335
is($nf->format_price(Math::BigFloat->bpi(100), 4, "\$"), '$ 3.1416');

t/round.t

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ use constant PI => 4*atan2(1,1);
1414
ok(compare_numbers(round(0), 0), 'identity 0');
1515
ok(compare_numbers(round(1), 1), 'identity 1');
1616
ok(compare_numbers(round(-1), -1), 'identity -1');
17+
ok(compare_numbers(round(1,0,-1), 1), 'floor 1');
18+
ok(compare_numbers(round(1,0, 0), 1), 'normal 1');
19+
ok(compare_numbers(round(1,0,+1), 1), 'ceil 1');
20+
ok(compare_numbers(round(-1,0,-1), -1), 'floor -1');
21+
ok(compare_numbers(round(-1,0, 0), -1), 'normal -1');
22+
ok(compare_numbers(round(-1,0,+1), -1), 'ceil -1');
23+
ok(compare_numbers(round(1.1,0,-1), 1), 'floor 1.1');
24+
ok(compare_numbers(round(1.1,0, 0), 1), 'normal 1.1');
25+
ok(compare_numbers(round(1.1,0,+1), 2), 'ceil 1.1');
26+
ok(compare_numbers(round(-1.1,0,-1), -2), 'floor -1.1');
27+
ok(compare_numbers(round(-1.1,0, 0), -1), 'normal -1.1');
28+
ok(compare_numbers(round(-1.1,0,+1), -1), 'ceil -1.1');
29+
ok(compare_numbers(round(-1.6,0,-1), -2), 'floor -1.6');
30+
ok(compare_numbers(round(-1.6,0, 0), -2), 'normal -1.6');
31+
ok(compare_numbers(round(-1.6,0,+1), -1), 'ceil -1.6');
1732
ok(compare_numbers(round(PI,2), 3.14), 'pi prec=2');
1833
ok(compare_numbers(round(PI,3), 3.142), 'pi prec=3');
1934
ok(compare_numbers(round(PI,4), 3.1416), 'pi prec=4');

0 commit comments

Comments
 (0)