Cette réponse à la première question liée a la ligne presque jetable à la fin:
Voir aussi %g
pour arrondir à un nombre spécifié de chiffres significatifs.
Vous pouvez donc simplement écrire
printf "%.2g" "$n"
(mais voir la section ci-dessous sur le séparateur décimal et les paramètres régionaux, et notez que les non-Bash printf
n'ont pas besoin de prendre en charge %f
et %g
).
Exemples:
$ printf "%.2g\n" 76543 0.0076543
7.7e+04
0.0077
Bien sûr, vous avez maintenant une représentation des exposants de mantisse plutôt que la décimale pure, vous voudrez donc reconvertir:
$ printf "%0.f\n" 7.7e+06
7700000
$ printf "%0.7f\n" 7.7e-06
0.0000077
Mettre tout cela ensemble et l'envelopper dans une fonction:
# 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
}
(Remarque - cette fonction est écrite dans un shell portable (POSIX), mais suppose qu'elle printf
gère les conversions à virgule flottante. Bash a une fonction intégrée printf
qui le fait, donc ça va ici, et l'implémentation GNU fonctionne également, donc la plupart des GNU / Les systèmes Linux peuvent utiliser Dash en toute sécurité).
Cas de test
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
Résultats de test
.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
Une note sur le séparateur décimal et les paramètres régionaux
Tout le travail ci-dessus suppose que le caractère radix (également connu sous le nom de séparateur décimal) est .
, comme dans la plupart des environnements linguistiques anglais. D'autres paramètres régionaux utilisent à la ,
place, et certains shells ont un intégré printf
qui respecte les paramètres régionaux. Dans ces shells, vous devrez peut-être définir LC_NUMERIC=C
pour forcer l'utilisation de .
comme caractère Radix, ou écrire /usr/bin/printf
pour empêcher l'utilisation de la version intégrée. Cette dernière est compliquée par le fait que (au moins certaines versions) semblent toujours analyser les arguments en utilisant .
, mais imprimer en utilisant les paramètres régionaux actuels.
%f
/%g
, mais c'est l'printf
argument, et on n'a pas besoin d'un POSIXprintf
pour avoir un shell POSIX. Je pense que vous auriez dû commenter au lieu de le modifier.printf %g
ne peut pas être utilisé dans un script POSIX. Il est vrai que celaprintf
dépend de l' utilitaire, mais cet utilitaire est intégré dans la plupart des shells. L'OP étiqueté comme bash, donc utiliser un shebang bash est un moyen facile d'obtenir un printf qui prend en charge% g. Sinon, vous devrez ajouter un en supposant que votre printf (ou le printf intégré à votresh
if yprintf
est intégré) supporte le non standard (mais assez courant)%g
...dash
a un intégréprintf
(qui prend en charge%g
). Sur les systèmes GNU,mksh
c'est probablement le seul shell de nos jours qui n'aura pas de fonction intégréeprintf
.bash
) et reléguer une partie de cela aux notes - cela semble-t-il correct maintenant?printf "%.3g\n" 0.400
donne 0,4 et non 0,400TL; DR
Copiez et utilisez simplement la fonction
sigf
dans la sectionA reasonably good "significant numbers" function:
. Il est écrit (comme tout le code dans cette réponse) pour fonctionner avec dash .Il donnera l'
printf
approximation de la partie entière de N avec des$sig
chiffres.À propos du séparateur décimal.
Le premier problème à résoudre avec printf est l'effet et l'utilisation de la "marque décimale", qui aux États-Unis est un point, et en DE est une virgule (par exemple). C'est un problème car ce qui fonctionne pour certains paramètres régionaux (ou shell) échouera avec certains autres paramètres régionaux. Exemple:
Une solution courante (et incorrecte) consiste à définir
LC_ALL=C
la commande printf. Mais cela définit la marque décimale à un point décimal fixe. Pour les paramètres régionaux où une virgule (ou autre) est le caractère couramment utilisé qui pose problème.La solution consiste à découvrir à l'intérieur du script du shell qui l'exécute quel est le séparateur décimal local. C'est assez simple:
Suppression des zéros:
Cette valeur est utilisée pour modifier le fichier avec la liste des tests:
Cela rend les exécutions sur n'importe quel shell ou locale automatiquement valides.
Quelques notions de base.
Il devrait être intuitif de couper le nombre à formater avec le format
%.*e
ou même%.*g
printf. La principale différence entre l'utilisation%.*e
ou la%.*g
façon dont ils comptent les chiffres. L'une utilise le nombre total, l'autre a besoin du nombre moins 1:Cela a bien fonctionné pour 4 chiffres significatifs.
Une fois que le nombre de chiffres a été coupé du nombre, nous avons besoin d'une étape supplémentaire pour formater les nombres avec des exposants différents de 0 (comme c'était le cas ci-dessus).
Cela fonctionne correctement. Le nombre de la partie entière (à gauche du séparateur décimal) n'est que la valeur de l'exposant ($ exp). Le nombre de décimales nécessaires est le nombre de chiffres significatifs ($ sig) moins le nombre de chiffres déjà utilisés dans la partie gauche du séparateur décimal:
Comme la partie intégrante du
f
format n'a pas de limite, il n'est en fait pas nécessaire de le déclarer explicitement et ce code (plus simple) fonctionne:Premier essai.
Une première fonction qui pourrait le faire de manière plus automatisée:
Cette première tentative fonctionne avec de nombreux nombres mais échouera avec les nombres pour lesquels le nombre de chiffres disponibles est inférieur au nombre significatif demandé et l'exposant est inférieur à -4:
Il ajoutera de nombreux zéros inutiles.
Deuxième procès.
Pour résoudre ce problème, nous devons nettoyer N de l'exposant et tous les zéros à la fin. Ensuite, nous pouvons obtenir la longueur effective des chiffres disponibles et travailler avec cela:
Cependant, cela utilise des mathématiques en virgule flottante, et "rien n'est simple en virgule flottante": Pourquoi mes chiffres ne s'additionnent-ils pas?
Mais rien en "virgule flottante" n'est simple.
Pourtant:
Pourquoi?:
Et, également, la commande
printf
est une fonction intégrée de nombreux obus.Quelles
printf
impressions peuvent changer avec la coque:Une fonction "nombres significatifs" raisonnablement bonne:
Et les résultats sont:
la source
Si vous avez déjà le numéro sous forme de chaîne, c'est-à-dire «3456» ou «0,003756», vous ne pouvez le faire qu'en utilisant la manipulation de chaînes. Ce qui suit est sur le dessus de ma tête, et n'est pas minutieusement testé, et utilise sed, mais considérez:
Où, fondamentalement, vous supprimez et enregistrez tout "-0,000" au début, puis utilisez une opération de sous-chaîne simple sur le reste. Une mise en garde à propos de ce qui précède est que les multiples 0 en tête ne sont pas supprimés. Je vais laisser cela comme un exercice.
la source