Perché la divisione in aritmetica bash calcola la maggior parte delle percentuali come 0?


15

Tentativo di arithemetica bash per uno script, ma $enon si aggiorna fino alla fine. L'output parla da solo.

max=5
for e in $(seq 1 1 $max); do 
    percent=$(( $e/$max*100 ))
    echo "echo $e / $max : = $percent"
done

Tl; DR: visualizza 1..5 in percentuale.

Produzione :

echo 1 / 5 : = 0
echo 2 / 5 : = 0
echo 3 / 5 : = 0
echo 4 / 5 : = 0
echo 5 / 5 : = 100

Perchè è questo?


1
Correlati: Bash fornisce numeri interi come output indipendentemente dall'input durante l'esecuzione di calcoli (anche se, a differenza del problema descritto qui, che non può essere risolto bene riordinando le operazioni.)
Eliah Kagan

Risposte:


24

bashimpossibile gestire l'aritmetica non intera. Ti darà il risultato corretto fintanto che tutte le espressioni sono numeri interi. Quindi è necessario evitare di ottenere un valore non intero da qualche parte nel calcolo.

Nel tuo caso, quando stai valutando 1 / 5, 2 / 5ecc. Crea i valori di zero interi nelle corrispondenze bash ad alcuni valori non interi e i risultati stanno risultando essere zero di conseguenza. La precedenza di divisione e moltiplicazione sono gli stessi e gli stessi operatori precedenti vengono sempre eseguiti da sinistra a destra quando vengono posizionati nell'espressione.

Un modo per aggirare sarà quello di fare prima la moltiplicazione e poi la divisione in modo che bash non debba mai gestire un valore non intero. L'espressione corretta sarà,

$ max=5; for e in $(seq 1 1 $max); do percent=$(( $e*100/$max )); echo "echo $e / $max : = $percent"; done
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100

1
La sottoespressione 1/5non crea un valore non intero . Crea il valore intero 0, poiché il risultato della divisione intera di 1 per 5 è 0. Ulteriori operazioni utilizzano correttamente quel valore di 0. Non è quello che intendeva l'OP, ma nessuna operazione crea un valore non intero e nessuna operazione fallisce.
Eliah Kagan,

@EliahKagan grazie per averlo sottolineato il difetto. Modificato.
souravc,

16

Bash non funziona molto bene in questo tipo di aritmetica ... Ecco il tuo problema:

$ echo $((1/5))
0
$ echo $((2/5))
0
$ echo $((4/5))
0
$ echo $((4/5))
0

Se è necessario gestire valori non interi, è possibile utilizzare bc

$ max=5; for e in $(seq 1 1 "$max"); do percent=$(bc <<< "scale=1 ; $e/$max*100") ; echo "echo $e / $max : = ${percent%.*}"; done
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100

(grazie a @Arronical per aver sottolineato come formattare l'output come numeri interi)


Lo esaminerò sicuramente, grazie ancora!
Cybex,

1
È possibile tagliare l' .0output modificando $percent l'eco in ${percent%.*}:)
Arronical

11

A differenza di bash, awk offre un'aritmetica a virgola mobile. Per esempio:

$ awk -v max=5 'BEGIN{for (e=1;e<=max;e++) print "echo " e " / " max " : = " 100*e/max}' 
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100

5

Provare

percent=$(( $e*100/$max ))

:)

Vedi la sezione VALUTAZIONE ARITMETICA di:

man bash

Supporta solo numeri interi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.