A Rounding Function in PERL

 

Because there is no rounding function in Perl, I wrote the following code on my job to round sums to two decimal places for financial reporting. I subsequently augmented the function to allow for user-defined precision from 0 on out. While I realize there are more succinct methods to accomplish this, they all rely on casting or using other functions (such as sprintf) in ways that might be undefined. I posted this initially as filler for my nascent site and have left it here because it's so over the top

The rounder routine takes two arguments --a double to be rounded and an integer value for the precision. The lines prior to the sub routine allow you to try it from the command line (this version is UNIX based but should work on windows with minor modifications --primarily, changing the direction of the slashes). Rename the text file with a perl (.pl) extension and pass the number and precison as arguments. For example: yourname.pl .234567 2 will round .234567 to two decimal places.

The routine is lengthy because its based on string manipulation, but it works and you are free to incorporate it into your own programs. Copy and paste the text below or you might wish to try it here first.

Enter a double(xxxx.yyyyyyyy):
(The integers are optional but a decimal point is required)

Enter the number of decimal places to round to:
(Precision can be anywhere from 0 to the number of decimal places in your double)

#!/usr/bin/perl
$victim=$ARGV[0];
$len=$ARGV[1];
&rounder($victim,$len);
print STDOUT "$victim\n";
sub rounder(@_){
	my($dot,$prec,$cut_off,$flag,$chg,$len,$new,@round,@flags,@finished);
	$dot= index($_[0],".");
	$prec= $_[1];
	if($prec != 0){
	$cut_off= $dot + $prec + 1;
	}
	else{
	$cut_off=$dot + $prec + 2;
		if(substr($_[0],$dot + 1,1) < 5){
			$chg="NO";
		}
		else{
			$chg="YES";
		}
	}
	$len = length($_[0]);
	$flag= "N";
	for($i=0; $i <$len; $i++){
		$x=substr($_[0],$i,1);
		push(@round,$x);
	}
	for ($i= $len -1 ; $i >= $cut_off; $i--){
		if ($round[$i]==0 && $flag eq "Y"){
		        if($round[$i - 1] ==9){
               		 	$round[$i - 1] = 0;
			}
			else{
				$round[$i - 1]++;
				$flag="N";
	        	}
		}
	
		elsif($round[$i] >= 5 ){
			if($round[$i - 1] ==9){
				$round[$i - 1] = 0;
				$flag="Y";
			}
			else{
				$round[$i - 1 ] ++; 
			}
		}
pop(@round);
  }
$last=$#round;
if($prec == 0){
	if($dot==1 && $round[$last -2] ==9 and $flag eq "Y"){
				push (@finished, 1,0);
				$short="";
				foreach $dec(@finished){
				$short .= $dec
				}
				$_[0]= $short;
				return $_[0];
			}

	if($round[$last] >= 5 && $dot != 0 && $chg eq "YES"){
			if($round[$last - 2] == 9){
			pop(@round);
	  		$flag="Y";
	  		push(@flags,$flag);
			}
			else{
			$round[$last -2]++;
			pop(@round);
			}

	}
	elsif($round[$last] >= 5 && $dot == 0){
		if( $chg eq "YES"){
			unshift(@round,1);
			pop(@round);
		}
		else{
			unshift(@round,0);
			pop(@round);
		}
	}
	elsif($round[$last] < 5 && $dot == 0){
		if($round[$last]==0 && $flag eq "Y"){
			unshift(@round,1);
			pop(@round);
			}
		else{
		unshift(@round,0);
		pop(@round);
		}
	}
	elsif($round[$last] ==0 && $dot == 0 && $flag eq "Y"){
		unshift(@round,1);
		pop(@round);	
	}
	else{
		pop(@round);
	}
}

if($round[$last] == 0 && $flag eq "Y"){
		if($prec == 1){
			if($dot == 0){
				push (@finished, 1);
				push(@finished, @round);
				$short="";
				foreach $dec(@finished){
				$short .= $dec
				}
				$_[0]= $short;
				return $_[0];
			}
		}
		elsif($round[$last - 1] == 9){
		$round[$last - 1]=0;
		push(@flags,$flag);
		if($dot == 0){
			push (@finished, 1);
			push(@finished, @round);
			$short="";
			foreach $dec(@finished){
				$short .= $dec
				}
				$_[0]= $short;
				return $_[0];

		}			
	}
	else{
		if($prec != 0){
			$round[$last - 1]++;
			$flag="N";
			push(@flags, $flag);
		}
	}
}

$check="off";
if($round[0] == 9){
	$round[0]=1;
	$check="on";
}
	for($i=$dot - 1; $i >= 0; $i--){
		if($flag eq "Y"){	
			if($round[$i] == 9){
				$round[$i] = 0;
				push(@flags, $flag);
			}
			else{	
				$round[$i]++;
				$flag="N";
				push(@flags, $flag);
			}
		}
		else{
			push(@flags, $flag);
		}
	}

pop(@flags); 
$flag=pop(@flags);
if($check eq "on"){
	if($flag eq "Y"){
		@finished =(1,0);
		for($i=1; $i<=$#round; $i++){
		$insert=$round[$i];
		push(@finished,$insert);
		}
	}
	else{
	  $round[0]=9;
          @finished=@round;	
	}
}
else{
@finished=@round;
}
if($prec==0){
	pop(@finished);
}
foreach $dig(@finished){
$new .= $dig
}
$_[0]=$new;
}