2016-04-17 6 views
1

In Mac-Terminal möchte ich eine große Anzahl runden.Wie eine große Zahl in Shell-Befehl runden?

Zum Beispiel

Bei 10^13. Platz:
1234567812345678 -> 1230000000000000

Oder bei 10^12. Platz:
1234567812345678 -> 1235000000000000

So würde Ich mag um den Ort anzugeben und dann die gerundete Zahl zu erhalten. Wie mache ich das?

+0

Das sieht mehr wie Abschneiden als Rundung, und könnte mit Zeichensubstitutionen mit Regexes getan werden. – mpez0

Antwort

2

können Sie verwenden arithmetic expansion:

$ val=1234567812345678 
$ echo $((${val: -13:1} < 5 ? val - val % 10**13 : val - val % 10**13 + 10**13)) 
1230000000000000 
$ echo $((${val: -12:1} < 5 ? val - val % 10**12 : val - val % 10**12 + 10**12)) 
1235000000000000 

Diese prüft, ob die höchstwertigen entfernte Ziffer 5 oder größer ist, und wenn es ist, die letzte nicht entfernten Stelle wird um eins erhöht; dann subtrahieren wir den Divisionsrest von dem (potentiell modifizierten) Anfangswert.

Wenn Sie nicht haben wollen, es auf diese Weise zu schreiben, können Sie es in einer kleinen Funktion wickeln:

round() { 
    echo $((${1: -$2:1} < 5 ? $1 - $1 % 10**$2 : $1 - $1 % 10**$2 + 10**$2)) 
} 

, die dann wie folgt verwendet werden kann:

$ round "$val" 13 
1230000000000000 
$ round "$val" 12 
1235000000000000 

Hinweis das Zitat $val ist hier nicht unbedingt notwendig, es ist nur eine gute Angewohnheit.

Wenn die Einzeiler zu kryptisch ist, ist dies eine besser lesbare Version dieselbe ist:

round() { 
    local rounded=$(($1 - $1 % 10**$2)) # Truncate 

    # Check if most significant removed digit is >= 5 
    if ((${1: -$2:1} >= 5)); then 
     ((rounded += 10**$2)) 
    fi 
    echo $rounded 
} 

Neben arithmetischer Erweiterung, dies nutzt auch Parameter Expansion einen Teil zu erhalten: ${1: -$2:1} steht für „nehmen $1, zählen Sie $2 von der Rückseite, nehmen Sie ein Zeichen ". Es muss ein Leerzeichen vor -$2 sein (oder muss in Klammern angegeben werden), da es sonst als eine andere Erweiterung interpretiert würde und überprüft, ob $1 nicht gesetzt oder null ist, was wir nicht wollen.

+1

Schön gemacht; viel eleganter als das, was mir einfällt, also habe ich meine reine "bash" -Lösung durch eine Verbindung zu deiner ersetzt. – mklement0

+0

Ich habe genau das, was ich wollte. Vielen Dank. – Isaac

0

Eigentlich, wenn Sie auf n signifikante Ziffern runden möchten, könnten Sie am besten bedient werden, indem Sie traditionelle Mathematik und Streicher mischen.

Serious Debugging wird dem Schüler links, aber das ist, was ich kam schnell mit für Bash-Shell und hoffen, MAC nahe genug:

 
function rounder 
{ 
    local value=$1; 
    local digits=${2:-3}; 

    local zeros="$(eval "printf '0%.0s' {1..$digits}")"; #proper zeros 
     # a bit of shell magic that repats the '0' $digits times. 

    if ((value > 1$zeros)); then 
     # large enough to require rounding 
     local length=${#value}; 
     local digits_1=$(($digits + 1));  #digits + 1 
     local tval="${value:0:$digits_1}"; #leading digits, plus one 
     tval=$(($tval + 5));   #half-add 
     local tlength=${#tval};   #check if carried a digit 
     local zerox=""; 
     if ((tlength > length)); then 
      zerox="0"; 
     fi 
     value="${tval:0:$digits}${zeros:0:$((length-$digits))}$zerox"; 
    fi 

    echo "$value"; 
} 

Sehen Sie, wie diese viel kürzer gemacht werden kann, aber das ist eine andere Übung für den Schüler.

Vermeiden von Fließkomma-Mathe aufgrund der inherit problems within.

Alle Sonderfälle, wie negative Zahlen, werden nicht behandelt.

+0

Anstelle von 'zeros =" $ (eval "printf ...', können Sie 'printf -v Nullen' verwenden, um denselben Effekt zu erzielen. –

1

awk ‚s [s]printf Funktion kann für Sie tun Runden innerhalb der Grenzen von doppelter Genauigkeit Gleitkomma-Arithmetik:

$ for p in 13 12; do 
    awk -v p="$p" '{ n = sprintf("%.0f", $0/10^p); print n * 10^p }' <<<1234567812345678 
done 
1230000000000000 
1235000000000000 

Für eine reine bash Implementierung finden Benjamin W.'s helpful answer.

Verwandte Themen