Этот ответ на первый связанный вопрос имеет почти одноразовую строку в конце:
См. Также %g
для округления до указанного числа значащих цифр.
Так что вы можете просто написать
printf "%.2g" "$n"
(но смотрите раздел ниже о десятичном разделителе и локали, и обратите внимание, что non-Bash printf
не нужно поддерживать %f
и %g
).
Примеры:
$ printf "%.2g\n" 76543 0.0076543
7.7e+04
0.0077
Конечно, теперь у вас есть представление с показателем мантиссы, а не чисто десятичное число, поэтому вы захотите преобразовать обратно:
$ printf "%0.f\n" 7.7e+06
7700000
$ printf "%0.7f\n" 7.7e-06
0.0000077
Собираем все это вместе и заключаем в функцию:
# Function round(precision, number)
round() {
n=$(printf "%.${1}g" "$2")
if [ "$n" != "${n#*e}" ]
then
f="${n##*e-}"
test "$n" = "$f" && f= || f=$(( ${f#0}+$1-1 ))
printf "%0.${f}f" "$n"
else
printf "%s" "$n"
fi
}
(Примечание: эта функция написана в переносимой (POSIX) оболочке, но предполагает, что она printf
обрабатывает преобразования с плавающей запятой. В Bash есть встроенная функция printf
, так что все в порядке, и реализация GNU также работает, поэтому большинство GNU / Linux системы могут безопасно использовать Dash).
Контрольные примеры
radix=$(printf %.1f 0)
for i in $(seq 12 | sed -e 's/.*/dc -e "12k 1.234 10 & 6 -^*p"/e' -e "y/_._/$radix/")
do
echo $i "->" $(round 2 $i)
done
Результаты теста
.000012340000 -> 0.000012
.000123400000 -> 0.00012
.001234000000 -> 0.0012
.012340000000 -> 0.012
.123400000000 -> 0.12
1.234 -> 1.2
12.340 -> 12
123.400 -> 120
1234.000 -> 1200
12340.000 -> 12000
123400.000 -> 120000
1234000.000 -> 1200000
Примечание о десятичном разделителе и локали
Вся работа выше предполагает, что символ радиуса (также известный как десятичный разделитель) .
, как в большинстве английских языков. ,
Вместо этого используются другие локали , а в некоторых оболочках есть встроенная printf
локаль. В этих оболочках может потребоваться установить LC_NUMERIC=C
принудительное использование .
символа-символа или написать, /usr/bin/printf
чтобы предотвратить использование встроенной версии. Последнее осложняется тем фактом, что (по крайней мере, некоторые версии), кажется, всегда анализируют аргументы, используя .
, но печатают с использованием текущих настроек локали.
%f
/%g
, но этоprintf
аргумент, иprintf
для POSIX-оболочки не требуется POSIX. Я думаю, что вы должны были прокомментировать, а не редактировать там.printf %g
нельзя использовать в сценарии POSIX. Это правда, этоprintf
зависит от утилиты, но эта утилита встроена в большинство оболочек. OP помечен как bash, поэтому использование bash shebang - один из простых способов получить printf, который поддерживает% g. В противном случае, вам нужно добавить Предположим , что ваш Е (или PRINTF встроенную команду из ваших ,sh
еслиprintf
есть встроенная там) поддерживает нестандартные (но довольно часто)%g
...dash
имеет встроенныйprintf
(который поддерживает%g
). В системах GNU,mksh
вероятно, единственная оболочка в наши дни, которая не имеет встроенной функцииprintf
.bash
) и перенести часть этого в заметки - теперь это выглядит правильно?printf "%.3g\n" 0.400
дает 0,4, а не 0,400TL; DR
Просто скопируйте и используйте функцию
sigf
в разделеA reasonably good "significant numbers" function:
. Написано (как и весь код в этом ответе) для работы с тире .Это даст
printf
приближение к целой части N с$sig
цифрами.О десятичном разделителе.
Первая проблема, которую нужно решить с помощью printf, - это эффект и использование «десятичной метки», которая в США - это точка, а в DE - это запятая (например). Это проблема, потому что то, что работает для некоторой локали (или оболочки), потерпит неудачу с другой локалью. Пример:
Одним из распространенных (и неправильных решений) является установка
LC_ALL=C
для команды printf. Но это устанавливает десятичную запятую к фиксированной десятичной запятой. Для локалей, где запятая (или другая) является часто используемым символом, который является проблемой.Решение состоит в том, чтобы выяснить внутри скрипта для запускающей его оболочки, что является десятичным разделителем локали. Это довольно просто:
Удаление нулей:
Это значение используется для изменения файла со списком тестов:
Это делает запуск в любой оболочке или локали автоматически действительным.
Некоторые основы.
Это должно быть интуитивно понятно, чтобы вырезать число, которое будет отформатировано с форматом
%.*e
или даже%.*g
printf. Основное различие между использованием%.*e
или тем%.*g
, как они считают цифры. Один использует полный счет, другому нужен счет меньше 1:Это работало хорошо для 4 значащих цифр.
После того, как количество цифр было вырезано из числа, нам нужен дополнительный шаг для форматирования чисел с показателями, отличными от 0 (как это было выше).
Это работает правильно. Счетчик целочисленной части (слева от десятичной отметки) является просто значением показателя степени ($ exp). Необходимое количество десятичных знаков - это число значащих цифр ($ sig) за вычетом количества цифр, уже использованного в левой части десятичного разделителя:
Поскольку неотъемлемая часть
f
формата не имеет ограничений, на самом деле нет необходимости явно объявлять ее, и этот (более простой) код работает:Первое испытание
Первая функция, которая может сделать это более автоматизированным способом:
Эта первая попытка работает со многими числами, но потерпит неудачу с числами, для которых количество доступных цифр меньше запрошенного значимого числа, а показатель степени меньше -4:
Это добавит много нулей, которые не нужны.
Второе испытание
Чтобы решить это, нам нужно очистить N показателя степени и любые конечные нули. Тогда мы можем получить эффективную длину доступных цифр и работать с этим:
Тем не менее, это использует математику с плавающей запятой, и «нет ничего простого в плавающей запятой»: почему мои числа не складываются?
Но в «плавающей точке» нет ничего простого.
Тем не мение:
Почему?:
И, кроме того, команда
printf
является встроенной из многих оболочек.Какие
printf
отпечатки могут измениться с оболочкой:Достаточно хорошая функция «значимых чисел»:
И результаты:
источник
Если у вас уже есть число в виде строки, то есть «3456» или «0,003756», то вы могли бы сделать это, только используя строковые манипуляции. Следующее не в моей голове, и не полностью проверено, и использует sed, но учтите:
Там, где в основном вы удаляете и сохраняете любые «-0.000» вещи в начале, затем используйте простую операцию с подстрокой для остальных. Одно замечание по поводу вышеизложенного состоит в том, что несколько ведущих нулей не удаляются. Я оставлю это как упражнение.
источник