2017-01-11 16 views
2

Рассмотрим следующий поплавок:Как сохранить точность отображения при преобразовании из научного в десятичное обозначение?

8.22120183514065e-05 

Теперь очевидно (s)printf является стандартная вещь, чтобы сделать здесь, но %f изменяет свою точность:

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

Использование сколь угодно большой точностью, скажем %.30f, добавляет дополнительные цифры (...6505):

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

Есть ли способ сохранить точность, присутствующую в научной нотации, не зная ее точной точности заранее?

+0

Гм, нет? Строка '8.22120183514065e-05' сама по себе представляет собой только конечное представление« истинного »значения, представленного, например, в стандарте IEEE 754. – mob

+0

Если вы просто не спрашиваете о строковых манипуляциях с строковыми значениями, в этом случае я снимаю свое возражение. – mob

+0

Полагаю, вы хотите добавить число в экспоненте к точности, поэтому, если это 'e-05', вы добавляете пять цифр к тому, что вы обычно имели (по умолчанию 15). Если это 'e03', вы бы добавили три и т. Д. Я не вижу, как извлечь потенциального« экспонента », не используя его вручную. – zdim

ответ

3

Я считаю, что вы спрашиваете, как укорачивать число, такое, что стробирование имеет такое же количество знаковых цифр, что и литерал кода, который произвел номер.

это невозможно. Число значащих цифр в литеральном коде, которое произвело число (если даже было такое буквальное), нигде не хранилось.

Проще говоря, нет никакого способа, чтобы получить

0.0000822120183514065 

или даже

8.22120183514065e-05 

из числа

0.0000822120183514065132508730204818903075647540390491485595703125 

из имеющейся информации.


Было бы иначе, если бы вы начали со строкой

8.22120183514065e-05 

или, если вы знали, требуемую точность таким образом, чтобы вы могли создать вышеуказанную строку, используя

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

Если у вас это, вы могли бы выполнить строковые манипуляции, чтобы переместить . по мере необходимости.

$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 
+0

Достаточно честный. Я надеялся на какой-то встроенный способ 'sprintf'-y сделать это. Ну что ж. – Zaid

+1

Из числа? Информации там нет. Из строки? Это может привести к потере точности. Лучше всего работать со строкой. Решение добавлено для ответа. – ikegami