in reply to Re^2: How to use the int-function?
in thread How to use the int-function?

Hello there, its me again, the Anonymous Monk! :-)

Starting a new day, I felt refreshed to wrestle with the rounding problem again. To get to the beef, here is my proposal to round numbers using the int-function:

#!/usr/bin/perl my $float = $ARGV[0] ? $ARGV[0] : 1.255; my $decimals = $ARGV[1] ? $ARGV[1] : 2; print &round( $float , $decimals ) . "\n"; sub round { my $float = shift; my $decimals = shift; my $int_leftShiftFloat = int( $float * 10**($decimals + 1) ); my $int_Round = int( ( $int_leftShiftFloat + 5 ) / 10 ); my $float_rightShiftInt = $int_Round / 10**$decimals; my $float_Result = $float_rightShiftInt; return $float_Result } sub round_ { my $float = shift; my $dec = shift; return int( ( int( $float * 10**($dec + 1) ) + 5 ) / 10 ) / 10**$dec }
I've provided to functionally identical versions of the sub, so you may pick up which one is easier for you to read.

In the long version of the sub, I've tried to use speaking variable names and left out any comments instead.

To make a general comment on this solution, I've followed the suggestions provided yesterday by my fellow monks, and made the computation using integers to avoid the floating point hassle. What do you think about it?

Replies are listed 'Best First'.
Re^4: How to use the int-function?
by kennethk (Abbot) on Jan 04, 2011 at 18:14 UTC
    A couple comments:
    1. my $decimals = $ARGV[1] ? $ARGV[1] : 2; won't perform as expected if the user enters 0. You will get expected behavior if you use my $decimals = defined $ARGV[1] ? $ARGV[1] : 2;. You should also swap your $float assignment.
    2. On the same point, if you have v5.10 or greater, you can use the 'defined or' operator (//)to do the same thing: my $decimals = $ARGV[1] // 2;
    3. The & is largely unnecessary in modern Perl; your code functions just fine without it.
    4. I'd be a little gun-shy about using the same variable name in the script level variables as in the subroutine level variables - it's easy to introduce a difficult bug.
    5. You can use list assignment rather than a series of shifts if you want to save a few key strokes, replacing

      my $float = shift; my $dec = shift;

      with

        my ($float, $dec) = @_;

      This is almost entirely cosmetic in this context.

    I would also point out your algorithm will still be subject to the whims of these small deviations between string and double precision representation. Consider the default case of:

    #!/usr/bin/perl use strict; use warnings; my $number = defined $ARGV[0] ? $ARGV[0] : 1.2549999999999999; my $places = defined $ARGV[1] ? $ARGV[1] : 2; printf "%.20f\n", $number; print round( $number , $places ), "\n"; sub round { my ($float, $decimals) = @_; my $int_leftShiftFloat = int( $float * 10**($decimals + 1) ); my $int_Round = int( ( $int_leftShiftFloat + 5 ) / 10 ); my $float_rightShiftInt = $int_Round / 10**$decimals; my $float_Result = $float_rightShiftInt; return $float_Result }
    which outputs:

    1.25499999999999990000 1.26