in reply to Re: int($x) gives strange result

in thread int($x) gives strange result

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

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:print int($x*100 + 0.5)/100 ;

we get: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) ;

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.14999999999999991118and 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.14999999999999999445where 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.23999999999999999112while

`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.13while

`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.

Comment onRe^2: int($x) gives strange resultSelectorDownloadCode