Un script shell compatible POSIX n'est requis que pour prendre en charge integer arithmétique utilisant le langage shell ("seule l'arithmétique des entiers longs signés est requise"), donc un shell pur la solution doit émuler arithmétique à virgule flottante :
item=30
total=70
percent=$(( 100 * item / total + (1000 * item / total % 10 >= 5 ? 1 : 0) ))
100 * item / totaldonne le tronqué résultat de l'entier division en pourcentage.1000 * item / total % 10 >= 5 ? 1 : 0calcule la 1ère décimale, et si elle est égale ou supérieure à 5, ajoute 1 au résultat entier afin de l'arrondir.- Notez qu'il n'y a non besoin de préfixer les références de variables avec
$à l'intérieur d'un développement arithmétique$((...)).
Si - contrairement à la prémisse de la question - utilisation d'utilitaires externes est acceptable :
awkoffre un simple solution, qui, cependant, s'accompagne de la mise en garde qu'il utilise un vrai binaire double précision valeurs à virgule flottante et peut donc donner des résultats inattendus en décimal représentation - par exemple, essayezprintf '%.0f\n' 28.5, ce qui donne28plutôt que le29attendu ):
awk -v item=30 -v total=70 'BEGIN { printf "%.0f\n", 100 * item / total }'
- Notez comment
-vest utilisé pour définir des variables pour leawkscript, qui permet une séparation nette entre les guillemets simples et donc littéralawkscript et toutes les valeurs qui lui sont transmises depuis le shell .
- En revanche, même si
bcest un utilitaire POSIX (et on peut donc s'attendre à ce qu'il soit présent sur la plupart des plates-formes de type Unix) et effectue une précision arbitraire arithmétique, il tronque invariablement les résultats, de sorte que arrondir doit être exécuté par un autre utilitaire;printf, cependant, même s'il s'agit en principe d'un utilitaire POSIX, il n'est pas nécessaire pour prendre en charge les spécificateurs de format à virgule flottante (comme ceux utilisés dansawkci-dessus), donc ce qui suit peut ou peut ne pas fonctionner (et n'en vaut pas la peine, étant donné le plus simpleawksolution, et étant donné que les problèmes de précision dus à l'arithmétique en virgule flottante sont de retour dans l'image) :
# !! This MAY work on your platform, but is NOT POSIX-compliant:
# `-l` tells `bc` to set the precision to 20 decimal places, `printf '%.0f\n'`
# then performs the rounding to an integer.
item=20 total=70
printf '%.0f\n' "$(bc -l <<EOF
100 * $item / $total
EOF
)"
Utilisez AWK (pas de bash-ismes) :
item=30
total=70
percent=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }")
echo $percent
43
Prendre 2 * le calcul du pourcentage d'origine et obtenir le modulo 2 de celui-ci fournit l'incrément pour l'arrondi.
item=30
total=70
percent=$((200*$item/$total % 2 + 100*$item/$total))
echo $percent
43
(testé avec bash, ash, dash et ksh)
Il s'agit d'une implémentation plus rapide que le déclenchement d'un coprocessus AWK :
$ pa() { for i in `seq 0 1000`; do pc=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }"); done; }
$ time pa
real 0m24.686s
user 0m0.376s
sys 0m22.828s
$ pb() { for i in `seq 0 1000`; do pc=$((200*$item/$total % 2 + 100*$item/$total)); done; }
$ time pb
real 0m0.035s
user 0m0.000s
sys 0m0.012s