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 / total
donne le tronqué résultat de l'entier division en pourcentage.1000 * item / total % 10 >= 5 ? 1 : 0
calcule 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 :
awk
offre 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 donne28
plutôt que le29
attendu ):
awk -v item=30 -v total=70 'BEGIN { printf "%.0f\n", 100 * item / total }'
- Notez comment
-v
est utilisé pour définir des variables pour leawk
script, qui permet une séparation nette entre les guillemets simples et donc littéralawk
script et toutes les valeurs qui lui sont transmises depuis le shell .
- En revanche, même si
bc
est 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 dansawk
ci-dessus), donc ce qui suit peut ou peut ne pas fonctionner (et n'en vaut pas la peine, étant donné le plus simpleawk
solution, 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