user3019319
user3019319

Reputation: 336

using sprintf to round float numbers

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

Answers (4)

ikegami
ikegami

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

user3147760
user3147760

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

Joop Eggen
Joop Eggen

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

woolstar
woolstar

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

Related Questions