Bagaimana cara mengubah angka floating point ke integer?


47

Apakah ini cara yang tepat untuk melakukan konversi float ke integer di bash? Apakah ada metode lain?

flotToint() {
    printf "%.0f\n" "$@"
}

5
Apa yang benar"?
Joseph R.

Saya hanya ingin memastikan .. apakah ini benar atau ada yang lebih baik dari ini?
Rahul Patil

2
%.0fakan dibulatkan ke atas atau ke bawah. Apakah itu yang kamu inginkan? Anda dapat menggunakan printf "%d\n" "$@" 2>/dev/nulluntuk memotong fraksi.
ott--

Jawaban:


70

pesta

Dalam bash, itu mungkin sama baiknya dengan yang didapat. Itu menggunakan builtin shell. Jika Anda membutuhkan hasil dalam variabel, Anda bisa menggunakan substitusi perintah, atau bashspesifik (meskipun sekarang juga didukung oleh zsh):

printf -v int %.0f "$float"

Anda bisa melakukannya:

float=1.23
int=${float%.*}

Tapi itu akan menghapus bagian pecahan alih-alih memberi Anda bilangan bulat terdekat dan itu tidak akan bekerja untuk nilai $floatsuka 1.2e9atau .12misalnya.

Perhatikan juga batasan yang mungkin karena representasi internal dari pelampung:

$ printf '%.0f\n' 1e50
100000000000000007629769841091887003294964970946560

Anda memang mendapatkan integer, tetapi kemungkinan besar Anda tidak akan dapat menggunakan integer itu di mana pun.

Juga, seperti yang dicatat oleh @BinaryZebra, dalam beberapa printfimplementasi (bash, ksh93, yash, bukan GNU, zsh, dash), itu dipengaruhi oleh lokal (pemisah desimal yang dapat .atau ,).

Jadi, jika float Anda selalu dinyatakan dengan periode sebagai pemisah desimal dan Anda ingin diperlakukan seperti itu dengan printfmengabaikan lokal pengguna yang menggunakan skrip Anda, Anda harus memperbaiki lokal ke C:

LC_ALL=C printf '%.0f' "$float"

Dengan yash, Anda juga dapat melakukan:

printf '%.0f' "$(($float))"

(Lihat di bawah).

POSIX

printf "%.0f\n" 1.1

tidak POSIX karena %ftidak perlu didukung oleh POSIX.

POSIXly, Anda dapat melakukan:

f2i() {
  awk 'BEGIN{for (i=1; i<ARGC;i++)
   printf "%.0f\n", ARGV[i]}' "$@"
}

Yang satu itu tidak terpengaruh oleh lokal (koma tidak bisa menjadi pemisah desimal awkkarena sudah karakter khusus dalam sintaksis di sana ( print 1,2, sama print 1, 2dengan meneruskan dua argumen ke print)

zsh

Dalam zsh(yang mendukung aritmatika titik mengambang (pemisah desimal selalu merupakan periode)), Anda memiliki rint()fungsi matematika untuk memberi Anda bilangan bulat terdekat sebagai pelampung (seperti dalam C) dan int()untuk memberi Anda bilangan bulat dari pelampung (seperti dalam awk). Jadi kamu bisa melakukan:

$ zmodload zsh/mathfunc
$ i=$((int(rint(1.234e2))))
$ echo $i
123

Atau:

$ integer i=$((rint(5.678e2)))
$ echo $i
568

Namun perhatikan bahwa sementara doubles dapat mewakili angka yang sangat besar, bilangan bulat jauh lebih terbatas.

$ printf '%.0f\n' 1e123
999999999999999977709969731404129670057984297594921577392083322662491290889839886077866558841507631684757522070951350501376
$ echo $((int(1e123)))
-9223372036854775808

ksh93

ksh93 adalah shell Bourne-like pertama yang mendukung aritmatika floating point. ksh93 mengoptimalkan substitusi perintah dengan tidak menggunakan pipa atau forking ketika perintah hanya dibangun dalam perintah. Begitu

i=$(printf '%.0f' "$f")

tidak bercabang. Atau lebih baik lagi:

i=${ printf '%.0f' "$f"; }

yang tidak bercabang dua juga tetapi tidak pergi semua kesulitan untuk menciptakan lingkungan subkulit palsu.

Anda juga dapat melakukan:

i=$((rint(f)))

Namun waspadalah terhadap:

$ echo "$((rint(1e18)))"
1000000000000000000
$ echo "$((rint(1e19)))"
1e+19

Anda juga bisa:

integer i=$((rint(f)))

Tapi seperti untuk zsh:

$ integer i=1e18
$ echo "$i"
1000000000000000000
$ integer i=1e19
$ echo "$i"
-9223372036854775808

Berhati-hatilah bahwa ksh93aritmatika titik apung menghormati pengaturan pemisah desimal di lokal (meskipun ,sebaliknya operator matematika ( $((1,2))akan menjadi 6/5 di lokal / Jerman ... lokal, dan sama dengan $((1, 2)), yaitu 2 di lokal Inggris) .

yash

yash juga mendukung aritmatika floating point tetapi tidak memiliki fungsi matematika seperti ksh93/ zsh's rint(). Anda dapat mengonversi angka menjadi bilangan bulat dengan menggunakan biner atau operator misalnya (juga berfungsi zshtetapi tidak di ksh93). Perhatikan bahwa itu memotong bagian desimal, itu tidak memberi Anda bilangan bulat terdekat:

$ echo "$((0.237e2 | 0))"
23
$ echo "$((1e19))"
-9223372036854775808

yash menghormati pemisah desimal lokal pada output, tetapi tidak untuk konstanta literal titik mengambang dalam ekspresi aritmatiknya, yang dapat menyebabkan kejutan:

$ LC_ALL=fr_FR.UTF-8 ./yash -c 'a=$((1e-2)); echo $(($a + 1))'
./yash: arithmetic: `,' is not a valid number or operator

Ini bagus karena Anda dapat menggunakan konstanta floating point dalam skrip Anda yang menggunakan periode dan tidak perlu khawatir bahwa itu akan berhenti bekerja di lokal lain, tetapi masih dapat menangani angka-angka seperti yang diungkapkan oleh pengguna selama seperti yang Anda ingat:

var=$((10.3)) # and not var=10.3
... "$((a + 0.1))" # and not "$(($a + 0.1))".

printf '%.0f\n' "$((10.3))" # and not printf '%.0f\n' 10.3

int=${float%.*}bekerja sangat baik di skrip bash saya (versi 3.2.57 (1) -release) di Mac (versi 10.13.4 (17E199)).
TelamonAegisthus

Formulasi pertama Anda gagal di GNU bash 4.4.19, misalnya: `` `$ echo $ maksimum 32.800 $ printf -v int% .0f" $ maksimum "bash: printf: 32.800: angka tidak valid` ``
Luís de Sousa

@ LuísdeSousa, lokal Anda mungkin memiliki ,radix desimal. Lihat bagian tentangLC_ALL=C
Stéphane Chazelas

Perhatikan bahwa jawaban ini menjawab pertanyaan yang berbeda: Bagaimana menghapus digit setelah titik, bukan apa yang ditanyakan: apa cara yang tepat untuk melakukan float to integer .
Isaac

Haruskah metode ini diterapkan pada pelampung sejumlah bit? Haruskah sama untuk float 23 bit atau 128 bit mantissa?
Isaac


5

Jawaban sebelumnya yang diajukan hampir benar: "Anda bisa melakukan:

float=1.23
int=${float%.*}

Tetapi itu akan menghapus bagian pecahan alih-alih memberi Anda bilangan bulat terdekat dan itu tidak akan bekerja untuk nilai $ float seperti 1.2e9 atau .12 misalnya .... "

Gunakan saja ${float%%.*}.

echo ${float%%.*}
1

3

Yang sangat sederhana adalah hacky

function float_to_int() { 
  echo $1 | cut -d. -f1    # or use -d, if decimals separator is ,
}

Output sampel

$ float_to_int 32.333
32
$ float_to_int 32
32

Itu mungkin untuk digunakan sed, tetapi cutsolusinya lebih sederhana. Saya lebih suka sedatau cutkarena mereka IMHO lebih tersedia pada target tertanam daripada awk(yang masih cukup umum melalui busyboxdaripada bc).
pevik

0

Terkait:

  1. bagaimana membuat float ke integer di awk ,
  2. Bagaimana cara membulatkan angka floating point di shell?

Melengkapi @ Stéphane Chazelas awk jawab:

float_to_integer_rounding_nearst() {
    awk 'BEGIN{for (i=1; i<ARGC;i++) printf "%.0f", ARGV[i]}' "$@";
}

float_to_integer_rounding_floor() {
    awk 'BEGIN{for (i=1; i<ARGC;i++) printf "%d", int( ARGV[i] )}' "$@";
}

pada awks saya, yang terakhir putaran menuju nol, bukan minus minus (sebagai "lantai" dapat dianggap berarti). Juga, putaran pertama membagi dua angka genap. Saya tidak yakin apakah itu berbeda dari apa yang dilakukan Bash printf, atau jika ada alasan lain untuk lebih memilih awk daripada yang builtin printf.
ilkkachu
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.