Source AN::Tools::Math: Difference between revisions

From Alteeve Wiki
Jump to navigation Jump to search
(Created page with '{{mod_header}} <source lang="perl"> </source> {{footer}}')
 
No edit summary
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{mod_header}}
{{mod_header}}
'''[https://alteeve.ca/AN/Tools/Math.pm Math.pm]'''


<source lang="perl">
<source lang="perl">
package AN::Tools::Math;
use strict;
use warnings;
our $VERSION="0.1.001";
my $THIS_FILE="Math.pm";
sub new
{
my $class=shift;
my $self={};
bless $self, $class;
return ($self);
}
# Get a handle on the AN::Tools object. I know that technically that is a
# sibling module, but it makes more sense in this case to think of it as a
# parent.
sub parent
{
my $self=shift;
my $parent=shift;
$self->{HANDLE}{TOOLS}=$parent if $parent;
return ($self->{HANDLE}{TOOLS});
}
# This takes a number and rounds it to a given number of places after the
# decimal (defaulting to an even integer). This does financial-type rounding.
### MADI: Does this handle "x.95" type rounding properly?
sub round
{
my $self=shift;
my $param=shift;
# This just makes the code more consistent.
my $an=$self->parent;
# Clear any prior errors as I may set one here.
$an->Alert->_set_error;
# Setup my numbers.
my $num=0;
my $places=0;
# Now see if the user passed the values in a hash reference or
# directly.
if (ref($param) eq "HASH")
{
# Values passed in a hash, good.
$num=$param->{number} ? $param->{number} : 0;
$places=$param->{places} ? $param->{places} : 0;
}
else
{
# Values passed directly.
$num=$param;
$places=defined $_[0] ? shift : 0;
}
# Make a copy of the passed number that I can manipulate.
my $rounded_num=$num;
# Take out any commas.
$rounded_num=~s/,//g;
# If there is a decimal place in the number, do the smart math.
# Otherwise, just pad the number with the requested number of zeros
# after the decimal place.
if ( $rounded_num =~ /\./ )
{
# Split up the number.
my ($real, $decimal)=split/\./, $rounded_num, 2;
# If there is anything other than one ',' and digits, error.
if (($real=~/\D/) || ($decimal=~/\D/))
{
$an->Alert->error({
fatal => 1,
title => "Illegal value in 'AN::Tools::Math->round()'",
message => "The passed real number: [$num] contains an illegal value. Only digits and one decimal place are allowed in the real number being rounded.",
code => 2,
file => "$THIS_FILE",
line => __LINE__
});
# Return nothing in case the user is blocking fatal
# errors.
return (undef);
}
# If the number is already equal to the requested number of
# places after the decimal, just return. If it's less, pad the
# needed number of zeros. Otherwise, start rounding.
if ( length($decimal) == $places )
{
# Equal, return.
return $rounded_num;
}
elsif ( length($decimal) < $places )
{
# Less, pad.
$rounded_num=sprintf("%.${places}f", $rounded_num);
}
else
{
# Greater than; I need to round the number. Start by
# getting the number of places I need to round.
my $round_diff=length($decimal)-$places;
# This keeps track of whether the next (left) digit
# needs to be incremented.
my $increase=0;
# Now loop the number of times needed to round to the
# requested number of places.
for (1..$round_diff)
{
# Reset 'increase'.
$increase=0;
# Make sure I am dealing with a digit.
if ( $decimal =~ /(\d)$/ )
{
my $last_digit=$1;
$decimal=~s/$last_digit$//;
if ( $last_digit > 4 )
{
$increase=1;
if ( $decimal eq "" )
{
$real++;
}
else
{
$decimal++;
}
}
}
}
if ( $places == 0 )
{
$rounded_num=$real;
}
else
{
$rounded_num=$real.".".$decimal;
}
}
}
else
{
# This is a whole number so just pad 0s as needed.
$rounded_num=sprintf("%.${places}f", $rounded_num);
}
# Return the number.
return ($rounded_num);
}
1;
</source>
</source>


{{footer}}
{{footer}}

Latest revision as of 03:24, 5 May 2013

 AN!Tools :: AN::Tools :: Source AN::Tools::Math

Math.pm

package AN::Tools::Math;

use strict;
use warnings;

our $VERSION="0.1.001";
my $THIS_FILE="Math.pm";


sub new
{
	my $class=shift;
	
	my $self={};
	
	bless $self, $class;
	
	return ($self);
}

# Get a handle on the AN::Tools object. I know that technically that is a
# sibling module, but it makes more sense in this case to think of it as a
# parent.
sub parent
{
	my $self=shift;
	my $parent=shift;
	
	$self->{HANDLE}{TOOLS}=$parent if $parent;
	
	return ($self->{HANDLE}{TOOLS});
}

# This takes a number and rounds it to a given number of places after the
# decimal (defaulting to an even integer). This does financial-type rounding.
### MADI: Does this handle "x.95" type rounding properly?
sub round
{
	my $self=shift;
	my $param=shift;
	
	# This just makes the code more consistent.
	my $an=$self->parent;
	
	# Clear any prior errors as I may set one here.
	$an->Alert->_set_error;
	
	# Setup my numbers.
	my $num=0;
	my $places=0;
	
	# Now see if the user passed the values in a hash reference or
	# directly.
	if (ref($param) eq "HASH")
	{
		# Values passed in a hash, good.
		$num=$param->{number} ? $param->{number} : 0;
		$places=$param->{places} ? $param->{places} : 0;
	}
	else
	{
		# Values passed directly.
		$num=$param;
		$places=defined $_[0] ? shift : 0;
	}
	
	# Make a copy of the passed number that I can manipulate.
	my $rounded_num=$num;
	
	# Take out any commas.
	$rounded_num=~s/,//g;
	
	# If there is a decimal place in the number, do the smart math.
	# Otherwise, just pad the number with the requested number of zeros
	# after the decimal place.
	if ( $rounded_num =~ /\./ )
	{
		# Split up the number.
		my ($real, $decimal)=split/\./, $rounded_num, 2;
		
		# If there is anything other than one ',' and digits, error.
		if (($real=~/\D/) || ($decimal=~/\D/))
		{
			$an->Alert->error({
				fatal	=>	1,
				title	=>	"Illegal value in 'AN::Tools::Math->round()'",
				message	=>	"The passed real number: [$num] contains an illegal value. Only digits and one decimal place are allowed in the real number being rounded.",
				code	=>	2,
				file	=>	"$THIS_FILE",
				line	=>	__LINE__
			});
			# Return nothing in case the user is blocking fatal
			# errors.
			return (undef);
		}
		
		# If the number is already equal to the requested number of
		# places after the decimal, just return. If it's less, pad the
		# needed number of zeros. Otherwise, start rounding.
		if ( length($decimal) == $places )
		{
			# Equal, return.
			return $rounded_num;
		}
		elsif ( length($decimal) < $places )
		{
			# Less, pad.
			$rounded_num=sprintf("%.${places}f", $rounded_num);
		}
		else
		{
			# Greater than; I need to round the number. Start by
			# getting the number of places I need to round.
			my $round_diff=length($decimal)-$places;
			
			# This keeps track of whether the next (left) digit
			# needs to be incremented.
			my $increase=0;
			
			# Now loop the number of times needed to round to the
			# requested number of places.
			for (1..$round_diff)
			{
				# Reset 'increase'.
				$increase=0;
				
				# Make sure I am dealing with a digit.
				if ( $decimal =~ /(\d)$/ )
				{
					my $last_digit=$1;
					$decimal=~s/$last_digit$//;
					if ( $last_digit > 4 )
					{
						$increase=1;
						if ( $decimal eq "" )
						{
							$real++;
						}
						else
						{
							$decimal++;
						}
					}
				}
			}
			if ( $places == 0 )
			{
				$rounded_num=$real;
			}
			else
			{
				$rounded_num=$real.".".$decimal;
			}
		}
	}
	else
	{
		# This is a whole number so just pad 0s as needed.
		$rounded_num=sprintf("%.${places}f", $rounded_num);
	}
	
	# Return the number.
	return ($rounded_num);
}

1;

 

Any questions, feedback, advice, complaints or meanderings are welcome.
Alteeve's Niche! Enterprise Support:
Alteeve Support
Community Support
© Alteeve's Niche! Inc. 1997-2024   Anvil! "Intelligent Availability®" Platform
legal stuff: All info is provided "As-Is". Do not use anything here unless you are willing and able to take responsibility for your own actions.