Zaid
Zaid

Reputation: 37146

How can I retain display precision when converting from scientific to decimal notation?

Consider the following float:

8.22120183514065e-05

Now obviously (s)printf is the standard thing to do here, but %f alters its precision:

printf "%f", 8.22120183514065e-05;    # 0.000082

Using an arbitrarily large precision, say %.30f, adds additional digits (...6505):

printf "%.30f", 8.22120183514065e-05; # 0.000082212081351406505000000000

Is there a way to maintain the precision present in the scientific notation without knowing its exact precision in advance?

Upvotes: 2

Views: 82

Answers (1)

ikegami
ikegami

Reputation: 385809

I believe you are asking how to stringify a number such that the stringification has the same number of signficant digits as the code literal that produced the number.

That's impossible. The number of significant digits in the code literal that produced the number (if there even was such a literal) is not stored anywhere.

Simply put, there's no way to get

0.0000822120183514065

or even

8.22120183514065e-05

from the number

0.0000822120183514065132508730204818903075647540390491485595703125

from the available information.


It would be different if you started with the string

8.22120183514065e-05

or if you knew the desired precision such that you could create the above string using

sprintf('%.*e', $significant_digits-1, $n)

If you had this, you could perform string manipulations to move the . as needed.

$n =~ s/^\d\K\.// ? $n =~ s/^\d+\Ke([+-]\d+)\z// : $n =~ s/^\d\Ke([+-]\d+)\z// or die;
my $exp = $1 + 1;
if    ( $exp <= 0         ) { $n = '0.' . ( '0' x -$exp ) . $n;  }
elsif ( $exp < length($n) ) { substr($n, $exp, 0, '.');          }
elsif ( $exp > length($n) ) { $n .= '0' x ( $exp - length($n) ); }

$ perl -e'printf "%.70f\n", 8.22120183514065e-05;'
0.0000822120183514065132508730204818903075647540390491485595703125000000

$ perl -e'printf "%.70e\n", 8.22120183514065e-05;'
8.2212018351406513250873020481890307564754039049148559570312500000000000e-05

$ perl -e'printf "%.*e\n", 14, 8.22120183514065e-05;'
8.22120183514065e-05

Upvotes: 3

Related Questions