My Coding > Programming language > Perl > Perl FAQ > Perl: Rounding, How to round float

Perl: Rounding, How to round float

Rounding is a bit complicated in Perl. This is related with non-ideal float presentation in memory and also with different techniques used for rounding

Problem with float presentation in Perl

Actually, this is not pure Perl problem — this is a general problem of float presentation in memory. Let's imagine, we would like to investigate different routines fro rounding in Perl and create some set of floats for tests. We will make floats in range[-2, 2] with step 0.1


for( my $i = -2; $i <=2; $i += 0.1 ) {
    	 printf '$i = '." $i\n";
	}

and … the result is shocking!

Perl count with 0.1 step
Perl count with 0.1 step
Output was editted for better and shorter presentation. It is possible to see that there are some errors in Perl floats stored in the memory.
Original image: 845 x 486

It is possible to see, that in the range [-1.0 and 1.0], the floats are slightly different from what we expect from our cycle. And this tiny difference will give very different results if we will use this data for rounding properties. Therefore we need to use explicit task for rounding, without preliminary calculations. And also it is possible to see, that despite we required to make floats up to 2.0 inclusively, Perl not use 2.0 as a last float, stopping at 1.9

Therefore in further tests we will use hand typed floats, to clearly understand how rounting is working in Perl


foreach my $i (
 -2.0, -1.9, -1.8, -1.7, -1.6,
 -1.5, -1.4, -1.3, -1.2, -1.1,
 -1.0, -0.9, -0.8, -0.7, -0.6,
 -0.5, -0.4, -0.3, -0.2, -0.1,
 0.0, 0.1, 0.2, 0.3, 0.4, 0.5,
 0.6, 0.7, 0.8, 0.9, 1.0, 1.1,
 1.2, 1.3, 1.4, 1.5, 1.6, 1.7,
 1.8, 1.9, 2.0
 )

But to make picture more clear I will remove some data which not give any extra information and use only these data: (-2, -1.9, -1.5, -1.4, -1, -0.9, -0.5, -0.4, -0.1, 0, 0.4, 0.5, 0.6, 0.9, 1, 1.4, 1.5, 1.9, 2)

Tools for rounding in Perl

There are a lot of different functions in Perl designed for rounding values and also you can write your own functions:

  • int() - convert float numbers to integers.
  • sprintf() - Converting of any data into string. Can be used to convert float into integer or float with any precision string and then automatic conversion back to numerical value.
  • floor() - is the largest integer not greater than given
  • ceil() - is the smallest integer not less than given
  • R1 = int($x + 0.5) - Correct mathematical rounding for positive $x
  • R2 = int($i + $i/abs($i*2 || 1)) - Correct mathematical rounding for any $x
  • Math::Round::round() - Correct mathematical rounding from Math::Round library


$i	 int()	sprintf	floor()	ceil()	R1	R2	round()
-2.0	-2	-2	-2	-2	-1	-2	-2
-1.9	-1	-2	-2	-1	-1	-2	-2
-1.5	-1	-2	-2	-1	-1	-2	-2
-1.4	-1	-1	-2	-1	 0	-1	-1
-1.0	-1	-1	-1	-1	 0	-1	-1
-0.9	 0	-1	-1	 0	 0	-1	-1
-0.5	 0	-0	-1	 0	 0	-1	-1
-0.4	 0	-0	-1	 0	 0	 0	 0
-0.1	 0	-0	-1	 0	 0	 0	 0
 0.0	 0	 0	 0	 0	 0	 0	 0
 0.4	 0	 0	 0	 1	 0	 0	 0
 0.5	 0	 0	 0	 1	 1	 1	 1
 0.6	 0	 1	 0	 1	 1	 1	 1
 0.9	 0 	 1	 0	 1	 1	 1	 1
 1.0	 1	 1	 1	 1	 1	 1	 1
 1.4	 1	 1	 1	 2	 1	 1	 1
 1.5	 1	 2	 1	 2	 2	 2	 2
 1.9	 1	 2	 1	 2	 2	 2	 2
2.0	 2	 2	 2	 2	 2	 2	 2

What to use for rounding in Perl

As you can see from this resulting table, the correct rounding can only be performed by Math::Round::round() and our R2 = int($i + $i/abs($i*2 || 1)) function. If you read more about Math::Round module, you can find a lot of different rounding techniques there.

Another interesting result - sprintf() - give proper rounding as well, but if you check rounding for 0.5 and 1.5 you will see, that this is so called Round half to even technique. This rounding is used for reduce bias in calculations.

How to round to given precision in Perl

Everything we’ve discuss here was rounding to ineger value, but what should we do, if we need to round for some precision after coma?


sub R2 {
 my ($i) = @_;
 return int($i + $i/abs($i*2 || 1));
}

sub R2_float {
 my ($X, $precision) = @_;
 return R2($X/$precision)*$precision
}
my $X = 1.3465345;
my $presize = 0.01;
printf("$X => ".R2_float($X, $presize)."\n" ); # 1.3465345 => 1.35

Or you can use precision in the sprintf() if you are happy to use round half to even procedure.


my $X = 1.3465345;
my $presize = 2;
my $X_round = sprintf("%.${presize}f", $X);
printf("$X => $X_round\n" ); # 1.3465345 => 1.35


Published: 2021-10-17 11:21:28
Updated: 2021-10-18 07:06:12

Last 10 artitles


9 popular artitles

© 2020 MyCoding.uk -My blog about coding and further learning. This blog was writen with pure Perl and front-end output was performed with TemplateToolkit.