Home » Perl » Perl – Infinity Multiplication – Decimal, Precise Float Calculation

In the previous chapter of infinity multiplication, we was discussing about storing two integer numbers in string format and how to apply mathematical multiplication formula on the two strings by multiplying one digit at a time to produce an answer.

When it’s come to applying a mathematical formula on numbers that contains decimal values, since computer stores integer binary value as a whole digit and their base increase by the power of two for every additional binary digit, it is not possible to inject a decimal into a correct binary position. Thus computer software will usually utilise a float point calculation formula on numbers which contains decimal values. Numbers with decimal values are stored as a float type in the computer. Thus numbers with decimal values usually describes as float when it’s related to programming or computer.

Float calculation formula can be very precise on certain values, nevertheless there are values which float calculation have to round off some of the digits on the right side of the decimal to get a near precision value. These type of rounding can be very minor in our daily life and likely to not affect our day to day mathematical calculation.

Nevertheless if we truly want to calculate a decimal value that can be thousand of digits long, is there a solution to that? This is the second chapter of “Infinity Multiplication”, “Decimal, Precise Float Calculation”.

To have a little hint on this infinity subject, most of my readers ask me if this is truly an infinity library and is this truly an infinity subject? Infinity is the subject that is very hard to explain in word. Since the values can be endless and since it is endless, can we really calculate it? This is my formula for calculating an infinity value “infC = infA = infAL” or can be represent with “C = A = AL”, which can also translate to “Infinity Calculation equal to Infinity Assumption and is equal to Infinity Alteration”. I will describe about the infinity formula and how to apply the formula with programming in the last chapter of infinity multiplication, division, adding and subtracting subject, which is chapter five. The last chapter on the infinity subject will only be available in full on my blog at http://kevinhng86.iblog.website and at Programming World.

For the next chapter of infinity multiplication which is chapter three that is scheduled to be release this week, the chapter will contain the optimised source for for multiplication that can be use for a production environment. With the source code, the tester which I used for testing hundreds of thousand of test cases against my code will also be release with the article. The only code that I do not release with the article is the benchmark test code, which test how fast the program run versus 100,000 or test 1,000,000 cases.

This article was written for the Perl Programming’s environment. The source code to this article was written for the Perl Programming’s environment. Information related to how to multiply two string of digits together without the decimal can be refer to from chapter one of this article, Infinity Multiplication, “Beyond Integer”.

How To Multiply Two String Of Digits Together With Precise Decimal Calculation

To simplify the explanation for this article, we are assuming that we are multiplying a string full of digits which name A to another string that is full of digits which name B. When multiplying two strings of digits together to get a result value, the first challenge we would face for applying the mathematical equation on the strings is the decimal value or the digits behind the decimal. For addition and subtracting we can split the digit in A string into two parts. One part can contain the digits before the decimal and the other part can contain the digits behind the decimal. However this technique will be very complicated when applying to multiplication. This is because every digits, whether if before or after the decimal in string A has to be multiply to every digit in string B.

If we split A string into two parts, it’s can get very complicated to manipulate each part of A string to properly multiply to each part of B string, consider that they are both carry a decimal value. It’s doable, however complicated. We can have another simple solution, of which we can remove the decimal from both A and B string and treat the string as if the string does not contain the decimal. After we finished with the mathematical multiplication procedure we can inject the decimal into the correct position for our answer string.

In my opinion, the method of injecting the decimal into the correct position is highly efficiency, is error proof and is simpler for loop controlling. This method is doable for multiplication due to how the decimal is placed in a multiplication procedure. Nevertheless lets first inspect the formula for calculating a decimal value in a mathematical multiplication procedure.

Formula: Calculating the decimal value for multiplication.
Step 1: Count the amount of digits behind the decimal in A string that is not a trailing zero. Remove trailing zero. 
Step 2: Count the amount of digits behind the decimal in B string that is not a trailing zero. Remove trailing zero.
Step 3: Add the amount of digits that are behind the decimal for both A and B string together.
        In my description, this value is call the total amount of digits that need to be behind the decimal in our answer string.
Step 4: Remove the decimal from both A and B string, then apply the multiplication procedure on A and B string.
Step 5: Once the answer string is ready, count from right to left of the answer string.
        Using the value from step three and place the decimal in front of the total amount of digits that need to be behind the decimal in our answer string.
        If the answer string contain less digits from the count of step three, add a zero to the beginning of the answer string.
Answer: After placing the decimal, we can remove leading and trailing zeroes. 
        Trailing zeroes in decimal value do not alter the value of the number.
        Leading zeroes in a whole number do not alter the value of the number.

Example: 0.1 x 0.1
Step 1:   (1 digits behind the decimal in A) + (1 digits behind the decimal in B) = (2 digits behind the decimal needed in the answer string)
Step 2:   1 x 1 = 1   |  Answer string:    1  
Step 3:   Answer string has only 1 digit, there need to be 2 digits behind the decimal. Add zero to beginning of the answer string 
          Answer string: 01
Step 4.        .01      | add zero in front of the decimal for formatting | answer string = 0.01
Answer:  0.1 * 0.1 = 0.01

Example: 9.125 x 33.100
Step 1:   (3 digits behind the decimal in A) + (1 digits behind the decimal in B) = (4 digits behind the decimal needed in the answer string)
Step 2:   9125 x 331 = 3020375 |  Answer string:    3023075  
Step 3:   Answer string has more digits than the total digits needed to be behind the decimal. No need to modify. 
          Answer string: 3020375
Step 4.        302.0375    
Answer:  9.125 * 33.100 = 302.0375

Example: 152 x 239.55
Step 1:   (0 digits behind the decimal in A) + (2 digits behind the decimal in B) = (2 digits behind the decimal needed in the answer string)
Step 2:   152 x 23955 = 3641160  |  Answer string:    3023075  
Step 3:   Answer string has more digits than the total digits needed to be behind the decimal. No need to modify. 
          Answer string: 3641160
Step 4.        36411.60 | remove trailing zero for formatting | answer string is 36411.6   
Answer:  152 * 239.55 = 36411.6

With the formula above put in perspective, calculating the decimal values in a multiplication procedure can be done through indexing the decimal. This method is more of a string manipulating method. The placement for the decimal is calculated before calculating any actual value. In my opinion, this method is the most simplest method for calculating the decimal for any multiplication procedure, and this method offers the best precision placement for the decimal value as this method calculates the exact spot the decimal have to be place at in the answer sting.

Beside the formula, we still have shortcuts. Any numbers multiply by one will return itself as the value and any numbers that multiply by zero will always be zero. There is another shortcut that I do not apply to this formula and that is any number that multiplied to a .1 or .01 and so on will only need the decimal placement to be moved over to the left.

Nevertheless lets look at some code block that we need for our multiplication for procedure with decimal supported. For the first procedure we will need to search for the decimal in both our A string and B string. If the decimal is found, we need the position of the decimal. If the decimal is not found within the string, it is preferable that we will get a “-1” as the value for our variable. We are also declaring a position variable to later calculate the decimal position for our answer string. In Perl, we can use the index() function on the string to get the position of the decimal in the string. If the string doesn’t contain a decimal, the index() function will return “-1” as a value.

my $aDecPos = index($a, ".");
my $bDecPos = index($b, ".");
my $oDecPos = 0;

To calculate how many digits behind the decimal in either our A or B string, we are subtracting the total length of the string to the decimal position in the string. Before getting the decimal position we have to remove any trailing zero in the string. This is why it is important that this code block below only executed if a decimal is found. Since we are removing trailing zeroes, we can have issues with string that contains only only zero digits behind the decimal. Thus if the total length of the string minus the decimal position go into the negative we have to set the value to zero. Zero also mean, there is not a digit behind the decimal.

After we calculated the amount of digits that are behind the decimal in the string, we are also removing the decimal. This code block below is demonstrating the previous procedures.

if ( $aDecPos > -1 ){
      $a =~ s/0+$//g ;
      $aDecPos = ( length($a) - 1 - $aDecPos) > 0? ( length($a) - 1 - $aDecPos) : 0;
      $a =~ s/[.]//g ; 
}

Since we removed the decimal and any trailing zero, if we want to use the shortcut of anything multiply by one will return itself as an answer, we would have to modify our code block for the shortcut procedure. This code block below will only execute if A string contained only a digit one and A string does not contain any digits behind the decimal, If we found that B string does in fact contained a decimal before this loop started, we placed the decimal back into B string at the correct position before we return B as the result.

To put the decimal into the correct position for B string, we get the digits from the zero position to the digit where the decimal is going to be place. To calculate where the decimal is going to be place, we get the total length of string B and subtract the value to the amount of digits B has after the decimal. We then add the decimal into the string and then we add the digits from where the decimal is placed to the end of the string. After placing the decimal and if the first character in B string is only the decimal, we can place a zero in front of the decimal. This is to correct input format such as “.123” or “.5774” or “.1111”.

After that we check if A and B is difference in positive and negative value. If they are, we can place a negative sign in front of B string and then we return B as our result. If B does not contain a decimal value, we simply return B. We also have to check for differences in negative and positive values in both A and B string. This code block below demonstrated the above procedures.

if ( $a eq "1" && $aDecPos < 1 ){
          $b = substr($b, 0, length($b) - $bDecPos) . "." . substr($b, length($b) - $bDecPos); 
          $b = substr($b, 0, 1) eq "."? "0" . $b : $b;
          return (($isaNeg ne $isbNeg)? "-" . $b : $b);
     }
     return (($isaNeg ne $isbNeg)? "-" . $b : $b);
}

After evaluating all the shortcuts for a multiplication procedure, we can then calculate the amount of digits we need to have behind the decimal in our answer string. This code block will only execute if either A or B was found with a decimal. If A is found with a decimal and B does not contained a decimal, the value for variable bDecPos would be a -1. Therefor if there was not a decimal in B we can set bDecPos as zero value. It’s would also mean, there is not a single digit behind the decimal in B string. We can then add up the total amount of digits behind the decimal in both A and B string together to get the total amount of digits that would need to be behind the decimal in our answer string. This code block below demonstrate the above procedure.

if ( $aDecPos > -1 || $bDecPos > -1 ){
     $aDecPos = $aDecPos > -1? $aDecPos : 0;
     $bDecPos = $bDecPos > -1? $bDecPos : 0;        
     $oDecPos = $aDecPos + $bDecPos;
}

After the multiplication procedure and when we have an answer string, we can evaluate and place the decimal at the correct position in our answer string. We first check to see whether if our answer string contained less digits than the amount of digits that would need to be behind the decimal. This scenario occurs in events where “0.01 x 0.01” or “0.3 x 0.3” and so on.

To produce a new answer string with a decimal placement, we would grab the digits from first position from the left side of our answer string to where the decimal is going to be place. We then add the decimal behind these digits. After adding the decimal into the correct position, we add the rest of the digits in our answer string to the right side of the decimal. This new string’s value is now assign as our answer string. To calculate where the decimal is going to be place, we would need to subtract the length of our answer string to the amount of digits needed to be behind the decimal.

We then trim off any leading and trailing zero in our answer string. If the decimal is the last character in the string and there is not a digit behind the decimal, we would also remove the decimal. If the decimal is the first character in our answer string, we would place a zero in front of the decimal.

This code block below demonstrate the previous mentioned procedure in the Perl Programming’s environment.

if ( $oDecPos > 0 ){
      $output = length($output) < $oDecPos? "0" . $output : $output;
      $output = substr($output, 0 , length($output) - $oDecPos) . "." . substr($output, length($output) - $oDecPos);
      $output =~ s/0+$//g;
      $output =~ s/^0+//g;
      $output =~ s/[.]$//g;
      $output = substr($output, 0, 1) eq "."? "0" . $output : $output;
}

This below is the full functional code for this article which written for the Perl Programming’s environment. The code is capable of calculating numbers which can contains more than thousand of digits with precise calculation for decimal.

Visit my blog at http://kevinhng86.iblog.website to find all my articles.

# Written by Kevin Ng
# The full tutorial on this subject can be found @ http://kevinhng86.iblog.website 
# This source code file is a part of Kevin Ng Z library.
# To use this source code in your project please give the copyright credit.
# This function only multiply and does not check to see if it is only number in the string, you must build a checker around it.
# Notice: Version one and two of any infinity code from the libZ library are prototype.
#         They are not meant for production environment due to efficentcy.
#         Although are prototype these script were tested and ran through 300,000+ test cases without producing any errors.
#         This is version two of infinity multiplication for Perl, now with precise decimal calculation.
use strict;

package libZ;

    sub infiX{     
        my ($a, $b) = @_;
        my $isaNeg = substr($a,0,1) eq "-"? 1 : 0;
        my $isbNeg = substr($b,0,1) eq "-"? 1 : 0;       
        $a =~  s/^[+-]+//g ;
        $b =~ s/^[+-]+//g;    
        $a =~ s/^0+//g;
        $b =~  s/^0+//g; 
        
        my $aDecPos = index($a, ".");
        my $bDecPos = index($b, ".");
        my $oDecPos = 0;
        
        if ( $aDecPos > -1 ){
            $a =~ s/0+$//g ;
            $aDecPos = ( length($a) - 1 - $aDecPos) > 0? ( length($a) - 1 - $aDecPos) : 0;
            $a =~ s/[.]//g ; 
        }
        if ( $bDecPos > -1 ){    
            $b =~ s/0+$//g ;
            $bDecPos = ( length($b) - 1 - $bDecPos) > 0? (length($b) - 1 - $bDecPos) : 0;
            $b =~ s/[.]//g ;
        }
        
        
        if ( (length($a) < 1 || length($b) < 1 ) ){
            return "0";    
        }
        
        if ( $a eq "1" && $aDecPos < 1 ){
            if ( $bDecPos > 0 ){
                $b = substr($b, 0, length($b) - $bDecPos) . "." . substr($b, length($b) - $bDecPos); 
                $b = substr($b, 0, 1) eq "."? "0" . $b: $b;
                return (($isaNeg ne $isbNeg)? "-" . $b : $b);
            }
            return (($isaNeg ne $isbNeg)? "-" . $b : $b);
        }

        if ( $b == "1" && $bDecPos < 1 ){
            if ( $aDecPos > 0 ){
                $a = substr($a, 0, length($a) - $aDecPos) . "." . substr($a, length($a) - $aDecPos); 
                $a = substr($a,0,1) eq "."? "0" . $a: $a;
                return (($isaNeg ne $isbNeg)? "-" . $a : $a);
            }
            return (($isaNeg ne $isbNeg)? "-" . $a : $a);
        }
        
        if ( $aDecPos > -1 || $bDecPos > -1 ){
            $aDecPos = $aDecPos > -1? $aDecPos : 0;
            $bDecPos = $bDecPos > -1? $bDecPos : 0;        
            $oDecPos = $aDecPos + $bDecPos;
        }
        
        my $temp = 0;
        my $outposition = 0;
        my $alen = length($a);
        my $blen = length($b);
        my $output = "";
        for (my $i = $blen - 1; $i > -1; $i--){
            my $z = int(substr($b,$i,1));
            my $leftover = 0;
            my $outtemp = "";
            my $aidx = $alen - 1;

            while ($aidx > -1){
                $temp = int(substr($a,$aidx,1)) * $z + $leftover;
                $leftover = ($temp > 9)? int(substr($temp,0,-1)) : 0;
                $outtemp = substr($temp, -1) . $outtemp;         
                $aidx--;
            }          
            $outtemp = ($leftover > 0)? $leftover . $outtemp : $outtemp;
        
            if (length($output) < 1){
                $output = $outtemp;
                $outtemp = "";
                $outposition = $outposition + 1;
            } else {
                my $tempadd = 0;
                my $remainder = 0;
                my $outlen = length($output) - 1;
                my $outidx = 0;
                my $cposition = 0;
                my $tempaddstr = "";
                my $x = 0;
                my $y = 0;

                for (my $idx = length($outtemp) - 1; $idx > -1; $idx--){
                    $cposition = ($outlen - $outidx - $outposition);
                    $x = int(substr($outtemp,$idx,1));
                    $y = ($cposition > -1 )? int(substr($output,$cposition,1)) : 0;
                    $tempadd = $x + $y + $remainder;
                    $remainder = ($tempadd > 9)? 1 : 0;
                    $tempaddstr = substr($tempadd, -1) . $tempaddstr;
                    $outidx = $outidx + 1;
                }

                $tempaddstr = ($remainder > 0) ? $remainder . $tempaddstr : $tempaddstr;
                $output = $tempaddstr . substr($output,  length($output) - $outposition  );

                $outtemp = "";
                $outposition = $outposition + 1;
            }   
        }
        
       if ( $oDecPos > 0 ){
            $output = length($output) < $oDecPos? "0" . $output : $output;
            $output = substr($output, 0 , length($output) - $oDecPos) . "." . substr($output, length($output) - $oDecPos);
            $output =~ s/0+$//g;
            $output =~ s/^0+//g;
            $output =~ s/[.]$//g;
            $output = substr($output, 0, 1) eq "."? "0" . $output : $output;
        }       
        
        if ( $isaNeg ne $isbNeg ){
            $output = "-" . $output;
        }
        
        return $output;
}


package main;
my $x = "-.999999999999999999999999999999990000000000000000000000000000000000";
my $y = "+000000000000000000000000000000000.99999999999999999999999999999999";

print libZ::infiX($x, $y)

Written by: kevinhng86
Published at: programming.world.edu
Permalink: http://programming.world.edu/2017/02/17/perl-infinity-multiplication-decimal-precise-float-calculation/

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*

Skip to toolbar