Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

How to use the int-function?

by Anonymous Monk
on Jan 03, 2011 at 14:49 UTC ( #880194=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Maybe some fellow monk can fill me in on how to use the int-function, with which perl cuts the decimals of a floating point number. Let me phrase my specific question as a debugger meditation: (perl -d -e 1, which explains, why there are no ";" between the commands)

$i = 1.255 $j = $i * 100 + 0.5 $j = $j * 10000000 <p> print $j <p> -> 1260000000 <p> print int ( $j ) <p> -> 1259999999

Is there a way to make perl behave as a humble user of arithmetics would have expected, without needing additional modules like Math::Round?

Replies are listed 'Best First'.
Re: How to use the int-function?
by kennethk (Abbot) on Jan 03, 2011 at 15:00 UTC
    int is actually doing what you expect; the issue is that finite decimals in general do not have a finite representation as a binary decimal - see What Every Computer Scientist Should Know About Floating-Point Arithmetic. While you can symbolically perform the operation in question, the actual representation is ever so slightly less than the exact answer. You don't see this with your print statement because perl runs the double precision value through an implicit sprintf to make the output pretty to a human. If we increase the precision on your output, we can see that the behavior makes sense:

    #!/usr/bin/perl use strict; use warnings; my $i = 1.255; my $j = $i * 100 + 0.5; print "$j\n"; printf "%.16e\n", $j;


    126 1.2599999999999999e+002

    If your result is sufficiently sensitive that this is problematic, you can use Math::BigFloat, but this is likely a lot more complexity than a real world application requires.

Re: How to use the int-function?
by moritz (Cardinal) on Jan 03, 2011 at 15:02 UTC
Re: How to use the int-function?
by ikegami (Pope) on Jan 03, 2011 at 19:42 UTC

    255/1000 is a periodic number in binary just like 1/3 is a periodic number in decimal. It would take infinite storage to store it as a floating point number, so it is stored approximately.

    $ perl -e'printf "%.20e\n", 1.255' 1.25499999999999989342e+00 $ perl -e'printf "%.20e\n", (1.255 * 100 + 0.5) * 10000000' 1.25999999999999976158e+09

    Is there a way to make perl behave as a humble user of arithmetics would have expected

    No. If you're using floats, there's no way to make both of these true since the input is the same:

    int(1259999999.999999) = 1259999999 int((1.255 * 100 + 0.5) * 10000000) = 1256000000

    Said humble user will be disappointed in one of the two cases. But maybe you're ok with the user being disappointed in the former case. Just apply a bit of rounding at the desired tolerance.

    ( $j = sprintf("%.6f", $j) ) =~ s/\..*//s;
Re: How to use the int-function?
by Ratazong (Monsignor) on Jan 03, 2011 at 15:04 UTC
    Kennethk gave good insight why int is not working as expected (by you). The easiest workaround is to do the rounding yourself, e.g. by
    print int ( $j + 0.5);
    HTH, Rata
      Thank you, fellow monks. While two of the above answers pointed out that int is exactly doing what its supposed to do, Rata followed the rather pragmatic approach to show how to actually use the int-function to get the expected result. Let me please try to hold awaken the interest in the practial approach, while peeking into the computer scientist material later.

      In the following view on the problem, I try to round the number 1.255, and try to get the result 1.26. How would you perform this with the typical on-board means, to which undenyingly int() belongs to?

      #!/usr/bin/perl my $i = 1.255; print int($i*100+.5)/100; print "\n";

      Please register, that the same code will do as expected when using "1.355" as value for $i.

        If you are going to be formatting a number for output, I would suggest you use the methods designed specifically to handle that - printf and sprintf. If you are rounding/truncating a value in the context of numerical manipulation, then the approach you take very much depends on your specific needs. There is a very rich field of academic study revolving around performing computations with discrete approximations.

        #!/usr/bin/perl use strict; use warnings; my $i = 1.255; my $j = $i * 100 + 0.5; $j *= 10000000; printf "%.0f\n", $j;



        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?

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://880194]
Approved by kennethk
Front-paged by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (5)
As of 2020-11-29 14:30 GMT
Find Nodes?
    Voting Booth?

    No recent polls found