Home > Software design >  Aggregate very big numbers by reading a file in perl
Aggregate very big numbers by reading a file in perl

Time:09-27

There is a file which contains more than 20 million records. I need to use perl to aggregate the numbers and print the TOTAL on the last line. The numbers that I am supposed to aggregate are very big numbers and they could be positive or negative. I am using bignum module of perl to aggregate the numbers. However, it is not showing the correct results. Please advise.

sample.txt (in reality, this file contains more than 20 million records):

12345678910111213.00
14151617181920212.12345
23242526272829301.54321
32333435363738394.23456
-41424344454647489.65432

Expected output (my perl one liner is showing incorrect TOTAL on the last line):

12345678910111213.00
14151617181920212.12345
23242526272829301.54321
32333435363738394.23456
-41424344454647489.65432
TOTAL=<<total_should_be_printed>>

The perl one liner I am using:

perl -Mbignum -ne 'BEGIN{my $sum=0;} s/\r?\n$//; $sum=$sum $_; print "$_\n"; END{print "TOTAL=$sum"."\n";}' sample.txt

The perl one-liner is showing the TOTAL as 40648913273951600.00 and this is INCORRECT.

enter image description here

EDIT: Following one-liner is showing 40648913273951631.2469 as answer. Now it is really getting weird......

perl -Mbignum -e 'my $num1=Math::BigFloat->new("12345678910111213.00"); my $num2=Math::BigFloat->new("14151617181920212.12345"); my $num3=Math::BigFloat->new("23242526272829301.54321"); my $num4=Math::BigFloat->new("32333435363738394.23456"); my $num5=Math::BigFloat->new("-41424344454647489.65432"); my $sum=$num1 $num2 $num3 $num4 $num5; print $sum."\n";'

CodePudding user response:

Please verify calculation based on Math::BigFloat module.

use strict;
use warnings;
use feature 'say';

use Math::BigFloat;

my $sum = Math::BigFloat->new(0);
$sum->precision(-5);

while( <DATA> ) {
    my $x = Math::BigFloat->new($_);
    $sum->badd($x);
    say $x;
}

say "\nSUM: $sum";

exit 0;

__DATA__
12345678910111213.00
14151617181920212.12345
23242526272829301.54321
32333435363738394.23456
-41424344454647489.65432

Output

12345678910111213
14151617181920212.12345
23242526272829301.54321
32333435363738394.23456
-41424344454647489.65432

SUM: 40648913273951631.24690

CodePudding user response:

The main job of the bignum pragma is to turn literal numbers into Math::BigInt objects. Once assigned to a variable, that variable will also be an object, and any arithmetic operations carried out using it will be done using Math::BigInt operator overloading.

Since you are reading values from a file, they won't automatically be converted into Math::BigInt values. So you need something else to be the object, in this case $sum. By initialising to the literal 0 value as you have done, $sum becomes an object. Unfortunately you declare my $sum within the scope of the BEGIN block. Outside of this scope, $sum refers to a different package variable, which hasn't been initialised into an object.

So you need to declare the variable outside of the BEGIN, or add a literal zero to it to coerce it into an object:

perl -Mbignum -lne' $sum = 0 $_; END {print $sum}'

  •  Tags:  
  • perl
  • Related