Using Perl to split a float

Why did I lose precision with split? My goal is to get only the fractional part, all this.

$a = 123456789.123456789;
@b = split(/\./, $a);
$baseDec = "." . $b[1];

      

The above is $ baseDec ==. 123457

But it gives the correct accuracy: Is it correct? CORRECTION: THIS GIVES THE SAME FIVE ACCURACY! I have not tested the code correctly. Excuse me!

$a = 123456789.123456789;
@b = split(/\./, $a);
$baseInt = $b[0];
$baseDec = $a - $baseInt;

      

Should I be using Math :: BigFloat?

Edit: $ a should be a string $a = "123456789.123456789";

and then the original code works. Until I figure out how to get Perl to work with longdouble, I cannot verify the original question. The answer seems to be that I have lost precision because $ a is stored in double (52 bits ~ 15 decimal digits as @Ben pointed out below). print $a

gives 123456789.123457

.

+3


source to share


4 answers


IF you are going to treat it like a string, do it all over the place. You wouldn't assign an unquoted string, right?



my $a = "123456789.123456789";

      

+2


source


I'm making a few assumptions based on context, but you lost precision because it 123456789.123456789

is in your program as a numeric literal, which the Perl compiler stores in longdouble $a

. Then, when @b = split(/\./, $a);

executed, it is $a

implicitly forced into the string before it can be split

. By default, the behavior of your longdouble (80 bits of precision ~ 21 decimal digits) doubles (52 bits ~ 15 decimal digits) and your number becomes 123456789.1234567

before it becomes "123456789.1234567"

.

As others have said, you can get around this by using sprintf()

. You don't need Math :: BigFloat unless you are using numbers greater than longdouble

, but you need to exercise caution about when and how your numbers are represented as strings.

* For fun, you can check the size of your datatypes by running perl -V

and find these lines in the section Compiler

:



intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16

      

If d_longdbl

none were defined or yours longdblsize

was no more than eight bytes, your numeric literal would be truncated at compile time, but it isn't, so we already know that your Perl installation is capable of

+4


source


You can use int

:

 $a = 123456789.123456789;
 $baseDec = $a - int($a);

      

+2


source


You can get it using sprintf:

my $a = 123456789.123456789;
my @b = split(/\./, sprintf("%.8f",$a));
my $baseDec = "." . $b[1];
print $baseDec;

      

+1


source







All Articles