Bagaimana cara membulatkan angka floating point di shell?


13

Bagaimana cara membulatkan nomor floating point IEEE 754 dengan benar di baris perintah?

Saya ingin menentukan ketepatan nomor keluaran - hitungan angka fraksional.

Pembulatan 6.66ke presisi 1harus memberi 6.7, misalnya. Lebih banyak dalam tabel di bawah ini:

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

Seharusnya bisa digunakan dalam shell interaktif, tetapi idealnya cukup kuat untuk menggunakannya dalam skrip shell produksi.


Saya akan menggunakan awk.
isomorfisma

Jawaban:


30

Membulatkan angka floating point

Apa yang dimaksud dengan "membulatkan angka floating point"?
Itu mudah, jelas ... Di mana buku matematika saya dari sekolah ...

Tidak, kami sudah tahu tidak ada yang terkait dengan angka floating point mudah:

Sebagai permulaan, ada beberapa mode pembulatan:

Membulatkan ke atas?
Membulatkan ke bawah?
Pembulatan ke nol?
Membulatkan ke terdekat - ikatan untuk genap?
Membulatkan ke terdekat - ikatan jauh dari nol?

Bagaimana cara menangani kasing sudut? Bagaimana cara mengetahui kasus sudut mana?

OK, sepertinya kita lebih baik menggunakan implementasi standar IEEE 754, dan biarkan sistem kami yang mengurusnya.

Untuk membulatkan angka floating point di shell, berdasarkan aritmatika floating point standar, kita membutuhkan tiga langkah:

  • Ubah teks input dari argumen baris perintah ke angka floating point standar.
  • Bulatkan angka floating point menggunakan implementasi IEEE 754 normal.
  • Memformat angka sebagai string untuk output.

Ternyata perintah shell printfdapat melakukan semua ini. Ini dapat digunakan untuk mencetak angka sesuai dengan spesifikasi format seperti yang dijelaskan dalam man 3 printf. Angka-angka dibulatkan secara implisit dengan cara standar jika diperlukan untuk format output:

Perintah

Membulatkan xke pdigit presisi dengan input sebagai argumen baris perintah:

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

Atau dalam pipa shell, dengan input xpada input standar, dan psebagai argumen:

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

Contoh:

$ 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

Perangkap buruk

Berhati-hatilah di tempat itu! Ini menentukan pemisah antara bagian integral dan fraksi - .seperti yang Anda harapkan.
Tetapi lihat sendiri apa yang terjadi di lokal Jerman, misalnya:

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

Ya, benar 6,667- enam koma enam enam tujuh. Itu pasti akan mengacaukan naskah Anda.
(Tetapi hanya untuk dua pelanggan di Jerman. Kecuali untuk mesin pengembang saat ini sedang melakukan debug untuk pelanggan ini.)

Lebih kuat

Untuk membuatnya lebih kuat, gunakan:

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

atau

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

Ini juga menggunakan /usr/bin/printfbukannya shell yang dibangun dari bashatau zshuntuk bekerja di sekitar inkonsistensi kecil dalam implementasi printfvarian, dan mencegah efek yang sangat kotor ketika, di lokal Jerman, LC_ALLdiatur, tetapi tidak diekspor. Kemudian, builtin menggunakan ,, dan /usr/bin/printfmenggunakan ....


Lihat juga %guntuk pembulatan ke sejumlah digit signifikan.


0

Mengikuti berhasil bagi saya.

#!/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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.