Come arrotondare i numeri in virgola mobile nella shell?


13

Come arrotondare correttamente i numeri in virgola mobile IEEE 754 sulla riga di comando?

Voglio specificare la precisione del numero di output - il conteggio delle cifre frazionarie.

L'arrotondamento 6.66alla precisione 1dovrebbe dare 6.7, per esempio. Più nella tabella qui sotto:

Value   Precision  Rounded
6.66    0          7
6.66    1          6.7
6.66    2          6.66
6.66    3          6.660
6.666   3          6.666
6.6666  3          6.667

Dovrebbe essere utilizzabile in una shell interattiva, ma idealmente abbastanza robusto per usarlo negli script della shell di produzione.


Vorrei usare awk.
Isomorfismi,

Risposte:


30

Arrotondamento di numeri in virgola mobile

Che cosa significa "arrotondare un numero in virgola mobile"?
È facile, ovviamente ... Dov'è il mio libro di matematica da scuola ...

No, sappiamo già che nulla di correlato ai numeri in virgola mobile è facile:

Per cominciare, ci sono più modalità di arrotondamento:

Arrotondamento verso l'alto?
Arrotondamento verso il basso?
Arrotondamento a zero?
Arrotondamento al più vicino - legami con il pareggio?
Arrotondamento al più vicino - legami a partire da zero?

Come gestire le custodie angolari? Come scoprire quali sono i casi d'angolo?

OK, sembra che dovremmo usare un'implementazione dello standard IEEE 754 e lasciare che il nostro sistema se ne occupi.

Per arrotondare un numero in virgola mobile nella shell, basato sull'aritmetica in virgola mobile standard, sono necessari tre passaggi:

  • Converti il ​​testo di input da un argomento della riga di comando in un numero a virgola mobile standard.
  • Arrotondare il numero in virgola mobile utilizzando la normale implementazione IEEE 754.
  • Formatta il numero come stringa per l'output.

Si scopre che il comando shell printfpuò fare tutto questo. Può essere utilizzato per stampare numeri secondo una specifica di formato come descritto in man 3 printf. I numeri sono arrotondati implicitamente nel modo standard se è richiesto per il formato di output:

Il comando

Arrotondare xalla pprecisione delle cifre con input come argomenti della riga di comando:

printf "%.*f\n" "$p" "$x"

O in una pipeline di shell, con input xsu input standard e pcome argomento:

echo "$x" | xargs printf "%.*f\n" "$p"

Esempi:

$ printf '%.*f\n' 0 6.66
7
$ printf '%.*f\n' 1 6.66
6.7
$ printf '%.*f\n' 2 6.66
6.66
$ printf '%.*f\n' 3 6.66
6.660
$ printf '%.*f\n' 3 6.666
6.666
$ printf '%.*f\n' 3 6.6666
6.667

Trappole cattive

Attenzione alle impostazioni locali! Specifica il separatore tra la parte integrale e la frazione - il ., come ci si può aspettare.
Ma vedi cosa succede in un locale tedesco, ad esempio:

$ LC_ALL=de_DE.UTF-8 printf '%.*f\n' 3 6.6666
6,667

Sì, è vero 6,667- sei virgola sei sei sette. Ciò rovinerebbe sicuramente la tua sceneggiatura.
(Ma solo per i due clienti in Germania. Ad eccezione delle macchine per sviluppatori attualmente in fase di debug per questi clienti.)

Più robusto

Per renderlo più robusto, utilizzare:

LC_ALL=C /usr/bin/printf "%.*f\n" "$p" "$x"

o

echo "$x" | LC_ALL=C xargs /usr/bin/printf "%.*f\n" "$p"

Questo utilizza anche al /usr/bin/printfposto della shell incorporata basho zshper ovviare a piccole incongruenze nell'implementazione delle printfvarianti e prevenire un effetto molto sporco quando, in una locale tedesca, LC_ALLè impostato, ma non esportato. Quindi, il builtin usa ,e /usr/bin/printfusa ....


Vedere anche %gper arrotondare a un numero specificato di cifre significative.


0

Il seguito ha funzionato per me.

#!/bin/bash
function float() {
bc << EOF
num = $1;
base = num / 1;
if (((num - base) * 10) > 1 )
    base += 1;
print base;
EOF
echo ""
}

float 3.2
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.