Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

Given that int() truncates, if you wanted the result rounded to two decimal places you might:

print int($x*100 + 0.5)/100 ;
but, as brother ikegami points out, the underlying problem is that most decimal fractions are not exact in binary floating form, which catches everybody out most of the time. So, if we run:
sub show { my ($y, $p) = @_ ; my $x = $y + 0 ; my $t = $p + 4 ; print "Original value = $y, format = %${t}.${p}f\n" ; printf " \$x = %${t}.${p}f\n", $x ; printf " \$x*100 = %24.20f (%%24.20f)\n", $x * 100 ; printf " int(\$x*100)/100 = %${t}.${p}f\n", int($x*100)/100 ; printf "int(\$x*100 + 0.5)/100 = %${t}.${p}f\n", int($x*100 + 0.5)/10 +0 ; } ; show("1.15", 20) ;
we get:
Original value = 1.15, format = %24.20f
                   $x =   1.14999999999999991118
               $x*100 = 114.99999999999998578915 (%24.20f)
      int($x*100)/100 =   1.13999999999999990230
int($x*100 + 0.5)/100 =   1.14999999999999991118
and we see that the binary floating value for 1.15 is just a little bit smaller, such that int(1.15*100) is 114 -- so what looks like a way of getting two decimal places is actually making things worse. Adding 0.5 to round rather than truncate gives us, in this case, the same value as we started with (assuming +ve values). So show("1.15", 2) gives:
Original value = 1.15, format = %6.2f
                   $x =   1.15
               $x*100 = 114.99999999999998578915 (%24.20f)
      int($x*100)/100 =   1.14
int($x*100 + 0.5)/100 =   1.15
...so int($x*100 + 0.5)/100 seems to do the trick -- but note that it only rarely returns a value which is an exact two decimal places.

Note that the multiplication and division are themselves introducing small rounding errors. Consider show("0.15", 20), which gives:

Original value = 0.15, format = %24.20f
                   $x =   0.14999999999999999445
               $x*100 =  15.00000000000000000000 (%24.20f)
      int($x*100)/100 =   0.14999999999999999445
int($x*100 + 0.5)/100 =   0.14999999999999999445
where the rounding after multiplying by 100 just happens to give exactly 15.

If you want to truncate to two places of decimals, then something like int($x*100 + 0.0000001)/100 will generally do the trick -- but this is assuming a certain amount about (a) the precision of the floating point, and (b) the precision to which you want to work. (And (c) that the numbers are +ve.)

Interestingly, using printf "%0.2f" isn't the same as doing explicit rounding. Consider show("0.235", 20), which gives:

Original value = 0.235, format = %24.20f
                   $x =   0.23499999999999998668
               $x*100 =  23.50000000000000000000 (%24.20f)
      int($x*100)/100 =   0.23000000000000000999
int($x*100 + 0.5)/100 =   0.23999999999999999112
while show("0.235", 2) gives:
Original value = 0.235, format = %6.2f
                   $x =   0.23
               $x*100 =  23.50000000000000000000 (%24.20f)
      int($x*100)/100 =   0.23
int($x*100 + 0.5)/100 =   0.24
... because 0.235 is held as 0.234999..., the "%.2f" format gives 0.23. The explicitly rounded value is probably more what was expected.

Even when the fraction is an exact binary fraction you can get unexpected results. show("0.125", 2) gives:

Original value = 0.125, format = %6.2f
                   $x =   0.12
               $x*100 =  12.50000000000000000000 (%24.20f)
      int($x*100)/100 =   0.12
int($x*100 + 0.5)/100 =   0.13
while show("0.375", 2) gives:
Original value = 0.375, format = %6.2f
                   $x =   0.38
               $x*100 =  37.50000000000000000000 (%24.20f)
      int($x*100)/100 =   0.37
int($x*100 + 0.5)/100 =   0.38
(0.125 = 1/8, 0.375 = 3/8 -- so both are exact binary floating point values.) You can see that "%.2f" has rounded 0.125 down to 0.12, but 0.375 up to 0.38. This is an effect of the default rounding mode ("round to even") in IEEE 754 standard floating point arithmetic.


In reply to Re^2: int($x) gives strange result by gone2015
in thread int($x) gives strange result by natol44

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2024-04-16 01:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found