Reputation: 336
I have the following perl code and want to round the last diget i used sprintf but i get some weird result when the last digest is 5 i expect the rounding to be up but the actual result is different below an example :
my $x = sprintf("%.1f",4.35); -> expected 4.4
my $y= sprintf("%.1f",4.36); -> expected 4.4
my $z = sprintf("%.1f",4.32); -> expected 4.3
print $x,"\n"; -> actual result 4.3 -> expected 4.4
print $y,"\n"; -> actual result 4.4 -> ok
print $z,"\n"; -> actual result 4.3 ->ok
how can i change the above code /or if there are another option to get the expected results ?
Upvotes: 2
Views: 3375
Reputation: 386331
4.35 would get rounded to 4.4, but you actually have something slightly less than 4.35.
35/100 is periodic in binary just like 1/3 is periodic in decimal.
$ perl -E'say sprintf "%.20g", 0.35'
0.34999999999999998
Of course, the same applies to 4 35/100.
$ perl -E'say sprintf "%.20g", 4.35'
4.3499999999999996
As such, 0.35 and 4.35 cannot be represented exactly using a floating point number.
The following solution relies on the fact that 0.5 isn't periodic in binary (0.5 = 1/2 = 2-1, a finite sum of powers of 2):
$ perl -E'
say sprintf("%.0f", sprintf("%.15g", $_ * 10)) / 10
for 4.35, 4.36, 4.32;
'
4.4
4.4
4.3
Upvotes: 6
Reputation:
Try Math::Round module to see if works for your needs.
use Math::Round qw( nlowmult nhimult );
print nhimult(.1, 4.35), "\n"; # 4.4
print nhimult(.1, 4.45), "\n"; # 4.5
print nlowmult(.1, 4.35), "\n"; # 4.3
print nlowmult(.1, 4.45), "\n"; # 4.4
Upvotes: 0
Reputation: 109597
The best way is not to use floating point, but multiply all by 10 here; especially if money is involved.
For imprecise calculations, one may just accept that 4.35 actually is 4.349999.
Upvotes: 1
Reputation: 5083
% perl -E 'printf("%.1f\n", $_) for ( 4.35, 4.350000001 )'
4.3
4.4
4.35
is actually 4.34999999999999999
A kludge might be:
% perl -E 'printf("%.1f\n", $_ + 1e-15) for ( 4.35, 4.350000001 )'
4.4
4.4
Upvotes: 2