Uno script shell conforme a POSIX è richiesto solo per supportare integer aritmetica usando il linguaggio della shell ("è richiesta solo l'aritmetica di interi lunghi con segno"), quindi una shell pura la soluzione deve emulare aritmetica in virgola mobile:
item=30
total=70
percent=$(( 100 * item / total + (1000 * item / total % 10 >= 5 ? 1 : 0) ))
100 * item / total
restituisce il troncato risultato dell'intero divisione in percentuale.1000 * item / total % 10 >= 5 ? 1 : 0
calcola la prima cifra decimale e, se è uguale o maggiore di 5, aggiunge 1 al risultato intero per arrotondarlo.- Nota come no è necessario prefissare i riferimenti alle variabili con
$
all'interno di un'espansione aritmetica$((...))
.
Se - in contraddizione con la premessa della domanda - uso di utilità esterne è accettabile:
awk
offre un semplice soluzione, che, tuttavia, viene fornita con l'avvertimento che utilizza un vero binario a doppia precisione valori in virgola mobile e potrebbero pertanto produrre risultati imprevisti in decimal rappresentazione - ad esempio, provaprintf '%.0f\n' 28.5
, che restituisce28
piuttosto che il previsto29
):
awk -v item=30 -v total=70 'BEGIN { printf "%.0f\n", 100 * item / total }'
- Notare come
-v
è usato per definire le variabili perawk
script, che consente una netta separazione tra le virgolette singole e quindi letteraleawk
script e tutti i valori che gli vengono passati dalla shell .
- Al contrario, anche se
bc
è un'utilità POSIX (e ci si può quindi aspettare che sia presente sulla maggior parte delle piattaforme simili a Unix) ed esegue precisione arbitraria in aritmetica, invariabilmente tronca i risultati, in modo che l'arrotondamento deve essere eseguito da un altro utilità;printf
, tuttavia, anche se in linea di principio è un'utilità POSIX, non è richiesta per supportare identificatori di formato a virgola mobile (come quelli usati all'interno diawk
sopra), quindi quanto segue potrebbe o meno funzionare (e non ne vale la pena, dato il più sempliceawk
soluzione, e dato che i problemi di precisione dovuti all'aritmetica in virgola mobile sono tornati in scena):
# !! 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
)"
Usa AWK (senza bash-ism):
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
Prendendo 2 * il calcolo percentuale originale e ottenendo il modulo 2 di esso fornisce l'incremento per l'arrotondamento.
item=30
total=70
percent=$((200*$item/$total % 2 + 100*$item/$total))
echo $percent
43
(testato con bash, ash, dash e ksh)
Questa è un'implementazione più veloce rispetto all'attivazione di un coprocesso 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