Subject: | Add new option: round_option (floor/ceil/normal) |
This is exact the module i was locking for, but there was a feature
missing: i want to decide what the rounding will do. Simple floor, ceil
or the normal thing.
Now here is a patch for that feature, but i am not sure if i understand
your extra-feature with the "+ .5 + 1e-14". Would not POSIX::floor/ceil
help in that solution? Perhaps my "1 - 1e-14" is also broken by design
... i dont know, but all tests are working and it was adobted from your
code.
Also i dont provide constants for the option, i am not sure if this is
desired. Using < 0 for floor, == 0 for normal and > 0 for ceil looks
very easy to understand and makes a constant needless.
Subject: | Number-Format-1.73.roundoption.patch |
diff -ru Number-Format-1.73/Format.pm Number-Format-1.73-roundoption/Format.pm
--- Number-Format-1.73/Format.pm 2009-09-26 01:47:51.000000000 +0200
+++ Number-Format-1.73-roundoption/Format.pm 2012-03-09 22:55:13.838757462 +0100
@@ -24,7 +24,7 @@
$number = $x->unformat_number($formatted);
use Number::Format qw(:subs);
- $formatted = round($number, $precision);
+ $formatted = round($number, $precision, $roundoption);
$formatted = format_number($number, $precision, $trailing_zeroes);
$formatted = format_negative($number, $picture);
$formatted = format_picture($number, $picture);
@@ -59,6 +59,7 @@
DECIMAL_DIGITS - number of digits to the right of dec point (def 2)
DECIMAL_FILL - boolean; whether to add zeroes to fill out decimal
NEG_FORMAT - format to display negative numbers (def ``-x'')
+ ROUND_OPTION - decide to floor or normal rounding or ceil
KILO_SUFFIX - suffix to add when format_bytes formats kilobytes (trad)
MEGA_SUFFIX - " " " " " " megabytes (trad)
GIGA_SUFFIX - " " " " " " gigabytes (trad)
@@ -100,6 +101,7 @@
DECIMAL_DIGITS = 2
DECIMAL_FILL = 0
NEG_FORMAT = '-x'
+ ROUND_OPTION = 0
KILO_SUFFIX = 'K'
MEGA_SUFFIX = 'M'
GIGA_SUFFIX = 'G'
@@ -127,6 +129,9 @@
C<format_number()> and C<format_price()> utilize this feature by
calling C<format_negative()> if the number was less than 0.
+C<ROUND_OPTION> is only used by C<round()>. A number less then 0 means
+floor, a number bigger then 0 ceil and a 0 stand for a normal rounding.
+
C<KILO_SUFFIX>, C<MEGA_SUFFIX>, and C<GIGA_SUFFIX> are used by
C<format_bytes()> when the value is over 1024, 1024*1024, or
1024*1024*1024, respectively. The default values are "K", "M", and
@@ -195,7 +200,7 @@
$N_CS_PRECEDES $N_SEP_BY_SPACE $P_SIGN_POSN $N_SIGN_POSN );
our @EXPORT_OTHER =
- qw( $DECIMAL_DIGITS $DECIMAL_FILL $NEG_FORMAT
+ qw( $DECIMAL_DIGITS $DECIMAL_FILL $NEG_FORMAT $ROUND_OPTION
$KILO_SUFFIX $MEGA_SUFFIX $GIGA_SUFFIX
$KIBI_SUFFIX $MEBI_SUFFIX $GIBI_SUFFIX );
@@ -242,6 +247,7 @@
our $DECIMAL_DIGITS = 2;
our $DECIMAL_FILL = 0;
our $NEG_FORMAT = '-x';
+our $ROUND_OPTION = 0;
our $KILO_SUFFIX = 'K';
our $MEGA_SUFFIX = 'M';
our $GIGA_SUFFIX = 'G';
@@ -276,6 +282,7 @@
decimal_digits => $DECIMAL_DIGITS,
decimal_fill => $DECIMAL_FILL,
neg_format => $NEG_FORMAT,
+ round_option => $ROUND_OPTION,
kilo_suffix => $KILO_SUFFIX,
mega_suffix => $MEGA_SUFFIX,
giga_suffix => $GIGA_SUFFIX,
@@ -448,18 +455,25 @@
##----------------------------------------------------------------------
-=item round($number, $precision)
+=item round($number, $precision, $roundoption)
Rounds the number to the specified precision. If C<$precision> is
omitted, the value of the C<DECIMAL_DIGITS> parameter is used (default
value 2). Both input and output are numeric (the function uses math
operators rather than string manipulation to do its job), The value of
-C<$precision> may be any integer, positive or negative. Examples:
-
- round(3.14159) yields 3.14
- round(3.14159, 4) yields 3.1416
- round(42.00, 4) yields 42
- round(1234, -2) yields 1200
+C<$precision> may be any integer, positive or negative. If C<$roundoption>
+is omitted, the value of the C<ROUND_OPTION> paramter is used (default
+value 0). Examples:
+
+ round(3.14159) yields 3.14
+ round(3.14159, undef, 1) yields 3.15
+ round(3.14159, undef, -1) yields 3.14
+ round(3.14159, 4) yields 3.1416
+ round(42.00, 4) yields 42
+ round(1234, -2) yields 1200
+ round(1234, -2, 1) yields 1300
+ round(1298, -2) yields 1300
+ round(1298, -2, -1) yields 1200
Since this is a mathematical rather than string oriented function,
there will be no trailing zeroes to the right of the decimal point,
@@ -471,22 +485,29 @@
sub round
{
- my ($self, $number, $precision) = _get_self @_;
- $precision = $self->{decimal_digits} unless defined $precision;
- $precision = 2 unless defined $precision;
- $number = 0 unless defined $number;
-
- my $sign = $number <=> 0;
- my $multiplier = (10 ** $precision);
- my $result = abs($number);
- my $product = $result * $multiplier;
+ my ($self, $number, $precision, $roundoption) = _get_self @_;
+ $precision = $self->{decimal_digits} unless defined $precision;
+ $roundoption = $self->{round_option} unless defined $roundoption;
+ $precision = 2 unless defined $precision;
+ $roundoption = 0 unless defined $roundoption;
+ $number = 0 unless defined $number;
+
+ my $sign = $number <=> 0;
+ my $multiplier = (10 ** $precision);
+ my $result = abs($number);
+ my $product = $result * $multiplier;
+ my $roundmodifier = $roundoption < 0 ? $sign < 0 ? 1 - 1e-14 : 0
+ : $roundoption == 0 ? 0.5 + 1e-14
+ : $roundoption > 0 ? $sign < 0 ? 0 : 1 - 1e-14
+ : croak "impossible block"
+ ;
croak "round() overflow. Try smaller precision or use Math::BigFloat"
if $product > MAX_INT;
# We need to add 1e-14 to avoid some rounding errors due to the
# way floating point numbers work - see string-eq test in t/round.t
- $result = int($product + .5 + 1e-14) / $multiplier;
+ $result = int($product + $roundmodifier) / $multiplier;
$result = -$result if $sign < 0;
return $result;
}
diff -ru Number-Format-1.73/t/round.t Number-Format-1.73-roundoption/t/round.t
--- Number-Format-1.73/t/round.t 2009-05-05 22:53:31.000000000 +0200
+++ Number-Format-1.73-roundoption/t/round.t 2012-03-09 22:57:56.123562220 +0100
@@ -14,6 +14,21 @@
ok(compare_numbers(round(0), 0), 'identity 0');
ok(compare_numbers(round(1), 1), 'identity 1');
ok(compare_numbers(round(-1), -1), 'identity -1');
+ok(compare_numbers(round(1,0,-1), 1), 'floor 1');
+ok(compare_numbers(round(1,0, 0), 1), 'normal 1');
+ok(compare_numbers(round(1,0,+1), 1), 'ceil 1');
+ok(compare_numbers(round(-1,0,-1), -1), 'floor -1');
+ok(compare_numbers(round(-1,0, 0), -1), 'normal -1');
+ok(compare_numbers(round(-1,0,+1), -1), 'ceil -1');
+ok(compare_numbers(round(1.1,0,-1), 1), 'floor 1.1');
+ok(compare_numbers(round(1.1,0, 0), 1), 'normal 1.1');
+ok(compare_numbers(round(1.1,0,+1), 2), 'ceil 1.1');
+ok(compare_numbers(round(-1.1,0,-1), -2), 'floor -1.1');
+ok(compare_numbers(round(-1.1,0, 0), -1), 'normal -1.1');
+ok(compare_numbers(round(-1.1,0,+1), -1), 'ceil -1.1');
+ok(compare_numbers(round(-1.6,0,-1), -2), 'floor -1.6');
+ok(compare_numbers(round(-1.6,0, 0), -2), 'normal -1.6');
+ok(compare_numbers(round(-1.6,0,+1), -1), 'ceil -1.6');
ok(compare_numbers(round(PI,2), 3.14), 'pi prec=2');
ok(compare_numbers(round(PI,3), 3.142), 'pi prec=3');
ok(compare_numbers(round(PI,4), 3.1416), 'pi prec=4');