Working With Number – Infinity Adding – Decimal, Precise Float Calculation – Perl

The previous chapter of Infinity Adding was discussing in regard to storing two integer numbers in string format and how to apply a mathematical addition equation on the two strings of digits by adding one digit at a time to produce an answer.

When we are discussing in regard to a mathematical calculation, one of the important components is how to deal with numbers that have a fractional part value. In the computer world, numbers that carry a fractional part are often called float or floating point values. When calculating a floating point value, a majority of computer’s program will not calculate the precise value. Computer’s program will often round a floating point value to get a near precise calculation. Sometimes even on basic mathematical equations that only involved addition, the computer can’t generate a precise floating point calculation.

For example, when you are working within the Perl five point two’s environment and apply this mathematical equation of -589838.669871 + 594959.819146. Perl will give you the answer of 5121.14927499997. Nevertheless, if we were to calculate the answer manually, we know that the correct answer is 5121.149275. This type of method is the computer’s standard basic method for calculating values with a decimal placement. However, what happened if we truly need a precise decimal point value to be calculated without rounding the answer, and the numbers that involve in the equation have the possibility to have more than five hundred digits behind the decimal? This is chapter two of Infinity Adding, “Decimal, Precise Float Calculation”.

Note: The source code of chapter one and two of the Infinity Addition subject are prototypes. The prototypes purposes are to demonstrate the procedures for adding large numbers together. They are not meant for a production environment due to efficiency issue. Chapter three will have a production version of the source code.

This is a sample log of the result I got from executing the script that attaches to this article compare to Perl’s built-in float calculation. For each answer, the one of the left is Perl’s answer, and the one on the right is the answer provided by the script at the end of this article.

-7914.6278 + 7993.8935 
79.2656999999999 | 79.2657 

-8264.6537 + 7593.1775 
-671.476200000001 | -671.4762 

-351879.293545 + 357597.473673 
5718.18012799998 | 5718.180128
                                                                                                                                                                                                                                                                                                                                                                           
-834421.851012 + 834440.399814 
18.5488020000048 | 18.548802 
                                                                                                                        
-599665.612699 + 594651.327761 
-5014.28493800003 | -5014.284938   
                                                                                                                                                                                                                                                                                                                                                                           
-89419.851963 + 90050.467254 
630.615291000009 | 630.615291                                                                                                                          

-589838.669871 + 594959.819146 
5121.14927499997 | 5121.149275

-876748.441789 + 804645.220095 
-72103.2216939999 | -72103.221694                                                                                                                    

How To Add Two Floating Numbers Together In String Format

When storing numbers with a fractional part in string format, the first challenge we would have when we are applying a mathematical equation on the string is the digits behind the decimal. The string of number can be split into an array, where one index of the array holds the digits before the decimal and the other index holds the digits behind the decimal. Nevertheless, there is a high possibility that the amount of digit behind the decimal that each string has can vary. That can be solved by padding zeroes on the right of the shorter string to make both strings of number contain the exact same amount of digits behind the decimal.

Besides array, we can also split one string with the fractional part into two strings. After the split, one string can carry the digits before the decimal, and the other string can carry the digits behind the decimal. Thus, if we were going to add two strings of numbers that contain a fractional part together, we can have up to four strings. An example of this would be if we were to add A and B together where both A and B does contain a fractional part. Then we would split A into two. Where A1 can contain the digits before the decimal and A2 contains the digits behind the decimal. The same scenario would also apply to B1 and B2.

When it comes to calculating the digits behind the decimal, we can pad zeroes to the right of B2 if B2 is shorter than A2 to produce the equivalent amount of digits behind the decimal or vice-versa if A2 is shorter than B2. Another method to this would be, instead of padding zeroes, we can read the longer string first from the right. As long as the position we read is not less than or equal to the largest position of the shorter string, we would not read from the shorter string, instead, we would assign a value of zero in lieu of the shorter string’s digit. The addition formula for calculating the digits behind the decimal would be the same as calculating the digits before the decimal. We only have one additional procedure when it comes to adding values that are behind the decimal, and it is the final carry-over value from the equation have to be carried over to the equation of adding the two strings of digits that are before the decimal.

In this chapter, the majority of the information is in regard to the source code and explanations of the programming procedures for the source code.

Let first start with evaluating the input value of the string. First I use a conditional statement to test if the string is positive or negative in value by looking at the first character in the string. If the first character in the string is a negative sign, the variable isaNeg will be assigned with the value of one, of which can also be equal to a boolean value of true. For anything else, we assign a zero value to isaNeg. To get the character by position in Perl, we can use the substr() function.

Note that you can also use a boolean declaration here with a true or false value. Nevertheless, I used a digit value here for isaNeg in the prototype to demonstrates another method for evaluating and storing a true or false value.

my $isaNeg = substr($a,0,1) eq "-"? 1 : 0;

After evaluating whether if the string can be a negative value or not, I removed any plus or minus sign using a regex pattern to search for and remove any positive or negative sign at the beginning of the string. We would also want to remove leading zeroes. This is because leading zeroes before the decimal do not offer any additional value to the number.

$a =~  s/^[-+]+//g ;
$a =~  s/^0+//g ;

We would then need to initialize a third evaluation on the input. If the user input .123 without putting a zero in front of the decimal then we have to append a single zero to the front of the decimal. Therefore, we would evaluate whether if the first character of the string is a decimal. If it is a decimal, we add a zero to the string. If the first character is not a decimal, the string will be itself. We can just simply check the first position of the string to see whether if the position does contain a decimal using a regex pattern as a matching condition. I’m using a regex conditional statement here instead of Perl’s substr() function is because I just want to demonstrate an alternative method that can produce the same result for the same procedure.

$a = ($a =~ /^[.]{1}/)? "0" . $a: $a;

After the above evaluation, I then evaluate if the string does contain a decimal by using regex. If the string does contain a decimal. A variable name adec will be assigned with the all the digits of A. After that, I used regex to replace any digits before the decimal and including the decimal in the value of variable adec. Adec is now holding the digits behind the decimal. Then I removed all the digits behind the decimal and including the decimal from variable A. Variable A is now holding the digits before the decimal.

if ($a =~ m/[.]/){
     $adec = $a;
     $adec =~ s/^[0-9]{0,}[.]//;
     $a =~ s/[.][0-9]{0,}//;
}

When it comes to the strings that contain the digits behind the decimal, in the event where they do not contain a value, it would be very inefficiency to add the empty strings together. Also, the digits behind the decimal have to be added together first before the digits before the decimal. To be able to execute the equation efficiently, we would have to wrap our code block for adding the digits behind the decimal right before the code block for adding the digits before the decimal. On the first two lines of the below code, adeclen and bdeclen are the lengths of the strings that contain the digits behind the decimal. The if statement code block will only execute if either one of the lengths of the strings that contain the digits behind the decimal is higher than zero. In others word, either adec or bdec need to have a length for the code block to execute.

my $adeclen = length($adec); 
my $bdeclen = length($bdec);
if ($adeclen > 0 || $bdeclen > 0){
#Equation code goes here
}

While reading through the strings in a while loop or in a for loop, let assume the $i variable is the index count and we will use it as the read position value to get a digit from our strings. We would only read the string that contains the digits behind the decimal if the current position value in the $i variable is lower than the string’s maximum length. If the read position is within the string length, we would get a digit from that string at that position. Then we would convert the digit that we just obtained to an integer format. For anything else, we assign a value of zero in lieu of the string’s value.

$x = $i < $adeclen? int(substr($adec,$i,1)) : 0;

When calculating the carry-over value for the next equation, we simply evaluate if the temporary answer’s value is higher than nine. If the temporary result value is higher than nine then we would assign one to the carry-over value. In a mathematical addition equation that only involves two sets of digits then the carry-over value can never be larger than the value of one. In the temporary result value, for anything else that is below the value of nine, we would assign a zero value to the carry-over value.

When assigning a value to the output, it is a constant that we will always assign the rightmost digit from the temporary answer to the left of the output string. We can base our evaluations on two conditions to get the rightmost digit from the temporary answer. If the result value is higher than nine, we take a value of ten and subtract to the temporary result value. For anything else, we simply keep the result value. This method can only be applied to a mathematical addition equation. Also, we can only apply this method when adding only two sets of digits together. This is because the carry-over value can never be larger than a value of one.

$carryOver = $temp > 9? 1 : 0;
$temp = $temp > 9? $temp - 10 : $temp;
$output = $temp . $output;
$output = $aidx < 1 && $bidx < 1? $carryOver . $output : $output ;

After adding the entire two strings of digits behind the decimal together and the output string contained the full result value for the equation. We would then need to append a decimal point to the front of the output string before going into our next calculation. We then check for any zeroes to the right of the output string and remove all ending zeroes. As opposed to a whole number, trailing zeroes to the right side of the digits that are behind a decimal point do not provide any additional value. After removing all the zeroes, if the last character is only the decimal then we would also remove the decimal. The previous two procedures are done through regex on line two and three.

$output = "." . $output;
$output =~ s/[0]{1,}$//;
$output =~ s/[.]$//;

The above explanations are important code lines for adding a positive number to a positive number. Let discuss some important code lines for adding a positive number to a negative number. Or a negative number to a positive number. The principles for adding a positive value to a negative value can be described as subtracting the two values. The result value from this type of equation will carry the same negative or positive base as the larger value.

The example below is for the event of when the first string of input being the higher value string. In the first code line, we are subtracting the first string’s digit to the second string’s digit then to any carry-over value. For the second code line, we are assigning a carry-over value bases on a condition. If the result value is less than zero then we assign a value of one to the carry-over value. Otherwise, a value of zero is assigned to the carry-over value. This is easy to achieve when we are only dealing with two sets of digits. If we were dealing with more than two sets of digits then we would have to implement another method.

On the third code line, if the result value is less than zero, or in others word, a negative value. Then we would take a value of ten and add to the result value. Otherwise, we would not alter the result value. We then place the result value to the left of the output string, the code statement for this is on the fourth code line.

$temp = $x - $y - $carryOver;  
$carryOver = $temp < 0? 1 : 0;
$temp = $temp < 0? 10 + $temp : $temp;
$output = $temp . $output;

The above is if the first input string is higher in value. When it’s come to the second input string that is higher in value, the equation’s procedures do not change, nevertheless, there are differences between the two equation. When comparing the first line of code to the above equation’s procedure, we are still subtracting the first string’s digit to the second string’s digit. Nonetheless, instead of subtracting to the carry-over value, we are now adding to the carry-over value. The main change in how we are assigning the carry-over value is the temporary result value has to be larger than zero for us to assign a value of one to the carry-over value.

On the third code line below, the ending is very important. The temporary result value that does not need to be modified is now negative in value. If we are going to convert a negative integer value to a string type format and then placing the value to the output string, we would have a minus sign between every digit. Thus, we can either subtract the temporary result value to itself twice to reach its positive value, utilize the substr() function on the temporary result value to grab the rightmost digit, or simply, multiply the temporary result value to a negative value of one.

$temp = $x - $y + $carryOver;
$carryOver = $temp > 0? 1 : 0;
$temp = $temp > 0?  10 - $temp : $temp - $temp - $temp;
$output = $temp . $output;

The code is below is the full function of this tutorial.

Advertisement

# "Copyright Notice", please do not remove.
# Written by Kevin Ng
# The full tutorial on this subject can be found @ http://kevinhng86.iblog.website or http://programming.world.edu.
# Release date to http://programming.world.edu will lag one week after release on http://kevinhng86.iblog.website
# This source code file is a part of Kevin Ng's Z library.
# This source code is licenses under CCDL-1.0  A copy of CDDL1.0 can be found at https://opensource.org/licenses/CDDL-1.0
# End "Copyright Notice"

# Notice: This is version 2 of infiA from Kevin Ng's LibZ library.
#         This is not a production version and is a prototype.
#         This version has been tested, but would be too slow in a production environment.
#         This version of infiA support mathematical addition with the decimal.
package libZ;
    sub infiA{
        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;
        $a = ($a =~ /^[.]{1}/)? "0" . $a: $a;
        $b = ($b =~ /^[.]{1}/)? "0" . $b: $b;

        my $adec = "";
        my $bdec = "";
        
        if ($a =~ m/[.]/){
            $adec = $a;
            $adec =~ s/^[0-9]{0,}[.]//;
            $a =~ s/[.][0-9]{0,}//;
        }
        if ($b =~ m/[.]/){
            $bdec = $b;
            $bdec =~ s/^[0-9]{0,}[.]//;
            $b =~ s/[.][0-9]{0,}//;
        }
        
        my $aidx = length($a) - 1;
        my $bidx = length($b) - 1;
        my $adeclen = length($adec);
        my $bdeclen = length($bdec);
        my $carryOver = 0;
        my $temp = 0;
        my $x = 0;
        my $y = 0;
        my $output = "";
        

        if ($isaNeg == $isbNeg){
            if ($adeclen > 0 || $bdeclen > 0){
                my $i = $adeclen > $bdeclen? $adeclen - 1 : $bdeclen - 1 ;
                while ($i > -1){
                    $x = $i < $adeclen? int(substr($adec,$i,1)) : 0;
                    $y = $i < $bdeclen? int(substr($bdec,$i,1)) : 0;
                    $temp = $x + $y + $carryOver;
                    $carryOver = $temp > 9? 1 : 0;
                    $temp = $temp > 9? $temp - 10 : $temp;
                    $output = $temp . $output;
                    $i--;
                }
                $output = "." . $output;
                $output =~ s/[0]{1,}$//;
                $output =~ s/[.]$//;
                $temp = 0;
            }    
            
            while ($aidx > -1 || $bidx > -1){
                $x = $aidx > -1? int(substr($a,$aidx,1)) : 0;
                $y = $bidx > -1? int(substr($b,$bidx,1)) : 0;
                $temp = $x + $y + $carryOver;
                $carryOver = $temp > 9? 1 : 0;
                $temp = $temp > 9? $temp - 10 : $temp;
                $output = $temp . $output;
                $output = $aidx < 1 && $bidx < 1? $carryOver . $output : $output ;
                $aidx = $aidx - 1;
                $bidx = $bidx - 1;
            }
            $output =~ s/^0+//;
            $output = ($output =~ /^[.]{1}/) || length($output) < 1? "0" . $output : $output;
            return ($isaNeg == 1 && $isbNeg == 1)? "-" . $output : $output;
            
        } elsif ($isaNeg != $isbNeg){
            my $larger = length($a) > length($b) ? "a" : (length($b) > length($a)? "b" : "1");
            if ($larger eq "1"){
                $larger = "";
                for (my $i = 0; $i < length($a) ; $i++){
                    $larger = int(substr($a, $i)) > int(substr($b,$i))? "a" : $larger;
                    $larger = int(substr($b, $i)) > int(substr($a,$i))? "b" : $larger;
                    $i = length($larger) > 0? length($a) : $i;
                    $larger = $i eq length($a) - 1 && length($larger) < 1? "0" : $larger;
                }

                if (($adeclen > 0 || $bdeclen > 0) && $larger eq "0"){
                    my $max = $adeclen > $bdeclen? $adeclen : $bdeclen;
                    for (my $i = 0; $i < $max ; $i++){
                        $x = $i < $adeclen? int(substr($adec, $i,1)) : 0;
                        $y = $i < $bdeclen? int(substr($bdec, $i,1)) : 0;
                        $larger = ($x > $y)? "a": ($y > $x? "b" : "0");
                        $i = $larger eq "a" || $larger eq "b"? $max : $i;
                    }
                }               
            }         
            
            if ($larger eq "0"){
                return $larger;
            } elsif ($larger eq "a"){           
                if ($adeclen > 0 || $bdeclen > 0){
                    my $i = $adeclen > $bdeclen? $adeclen - 1 : $bdeclen - 1 ;
                    while ($i > -1){
                        $x = $i < $adeclen? int(substr($adec,$i,1)) : 0;
                        $y = $i < $bdeclen? int(substr($bdec,$i,1)) : 0;
                        $temp = $x - $y - $carryOver; 
                        $carryOver = $temp < 0? 1 : 0;
                        $temp = $temp < 0? 10 + $temp : $temp;
                        $output = $temp . $output;
                        $i--;
                    }
                    $output = "." . $output;
                    $output =~ s/[0]{1,}$//;
                    $output =~ s/[.]$//;
                    $temp = 0;
                }

                while ($aidx > -1){
                    $x = int(substr($a,$aidx,1));
                    $y = $bidx > -1? int(substr($b,$bidx,1)) : 0;
                    $temp = $x - $y - $carryOver;  
                    $carryOver = $temp < 0? 1 : 0;
                    $temp = $temp < 0? 10 + $temp : $temp;
                    $output = $temp . $output;
                    $aidx = $aidx - 1;
                    $bidx = $bidx - 1;      
                }
                $output =~  s/^0+// ;
                $output = ($output =~ /^[.]{1}/)? "0" . $output : $output;
                return $isaNeg == 1? "-" . $output : $output;
            } elsif ($larger eq "b"){
                if ($adeclen > 0 || $bdeclen > 0){
                    my $i = $adeclen > $bdeclen? $adeclen - 1 : $bdeclen - 1 ;
                    while ($i > -1){
                        $x = $i < $adeclen? int(substr($adec,$i,1)) : 0;
                        $y = $i < $bdeclen? int(substr($bdec,$i,1)) : 0;
                        $temp = $x - $y + $carryOver; 
                        $carryOver = $temp > 0? 1 : 0;
                        $temp = $temp > 0? 10 - $temp : $temp - $temp - $temp;
                        $output = $temp . $output;
                        $i--;
                    }
                    $output = "." . $output;
                    $output =~ s/[0]{1,}$//;
                    $output =~ s/[.]$//;
                    $temp = 0;
                }
                
                while ($bidx > -1){
                    $x = $aidx > -1? int(substr($a,$aidx,1)) : 0;
                    $y = int(substr($b,$bidx,1));
                    $temp = $x - $y + $carryOver;
                    $carryOver = $temp > 0? 1 : 0;
                    $temp = $temp > 0? 10 - $temp : $temp - $temp - $temp;
                    $output = $temp . $output;
                    $aidx = $aidx - 1;
                    $bidx = $bidx - 1;      
                }   
                $output =~  s/^0+// ;
                $output = ($output =~ /^[.]{1}/)? "0" . $output : $output;
                return $isaNeg == 0? "-" . $output : $output;
            }          
        }
    }

package main;

my $x = "99999999999999999999999999999999999999999999.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999";
my $y = "99999999999999999999999999999999999999999999.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999";

print libZ::infiA($x, $y);

Digiprove sealCopyright secured by Digiprove © 2017

This post was written by Kevin and was first post @ http://kevinhng86.iblog.website.
Original Post Name: "Working With Number – Infinity Adding – Decimal, Precise Float Calculation – Perl".
Original Post Link: http://kevinhng86.iblog.website/2017/02/06/working-with-number-infinity-adding-decimal-precise-float-calculation-perl/.

Advertisement


Random Article You May Like

4 thoughts on “Working With Number – Infinity Adding – Decimal, Precise Float Calculation – Perl

  1. Thank you for commenting, as this point in time and with this version. The result from a bigrat calculation and this function does not have any difference in advantage. This function and the formula however can be port into other platform that does not support something similar to bigrat

    Nevertheless bigrat does have another advantage over this script right now is how bigrat handle it’s memory and execution time.

    This is chapter two of this subject, I am just demonstrating the formula and the procedure call. The next chapter will talk about efficient the code. Stay tune on this subject, I guarantee that you will not be disappointed. There is a total of five chapter on this subject.

    1. Kevin, they may go by different names, but big integer or big rational math is such a common and basic problem that I believe every major programming language either has built-in support for it or comes with a standard library that does. You should just be looking for those rather than be reinventing this particular wheel. At least if you’re trying to propose something that people would actually use in real work rather than just demonstrating you can reinvent a common solution.

      1. Thank you Duncan for replying,

        In my opinion, programming and life it is, everything around us is a mathematical equation. This including how many step we take, how much light pass through our eyes to produce an image inside our brain, how much sleep we need and it’s infinity in subject.

        For programming it’s always good to understand principle behind how a mathematical equation work. After all if you break down all the information, in my opinion it’s only come to two things. That is handling digits and characters. Characters in some way are also digits.

        How do we draw stuff on screen, it is a mathematical equation if you break it’s down. How does a render engine work, the very basic of it’s is still base on two binary digits.

        Nevertheless beside mathematical, sometimes I do take things for granted. I do appreciate others people built in function because it’s ease my programming. But there is that single event where I have to convert a date to a unix timestamp while programming in Javascript. I couldn’t because the environment didn’t offer the getTime() function. It was not possible.

        I didn’t understand how a timestamp work. It was so common that everyone just use the built in library. So I end up have to build my own timestamp converter by studying all the variables that need to be input into an equation to get a timestamp value. That is when I realise it is good to use someone else library but if you have the time to study how it’s work, that’s can also help efficient knowledge for other programming procedure.

        I do believe there are some value in my article as of now. As you can see, this chapter alone demonstrate how string can be handle. How digits can be split convert and piece back together. How a mathematical equation can be apply to string of digits. There are also some insight on understanding how bigInt and bigRat work.

        To be honest though, I didn’t even know about bigInt or bigRat when I first idea to write this. You can call me a liar but that is the true of it. In the end though, these articles are fully for educational purpose. Sometimes it is interesting to know how a programming procedure work.

Leave a Reply

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

*
*