Kayan nokta sayısını tam sayıya nasıl dönüştürebilirim?


47

Bash'ta tamsayı dönüşümü yapmak için doğru yol bu mu? Başka bir yöntem var mı?

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

5
Doğru olan ne"?
Joseph R.

Sadece emin olmak istiyorum ... bu doğru mu, yoksa bundan daha mı iyi?
Rahul Patil

2
%.0fyukarı veya aşağı yuvarlayacaktır. İstediğin bu mu? printf "%d\n" "$@" 2>/dev/nullKesirleri kesmek için kullanabilirsiniz .
ott--

Yanıtlar:


69

darbe

In bash, muhtemelen alabileceği kadar iyi. Bu bir kabuk yerleşik kullanır. Eğer bir değişkenin sonucu gerekiyorsa, komut ikame veya kullanabilir bashspesifik (gerçi şimdi de desteklediği zsh):

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

Yapabilirsin:

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

Fakat bu size en yakın tamsayıyı vermek yerine kesirli kısmı ortadan kaldıracak ve bu $floatgibi 1.2e9ya da .12örneğin değerleri için işe yaramayacaktı .

Ayrıca, şamandıraların dahili gösterimi nedeniyle olası sınırlamaları not edin:

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

Bir tamsayı elde edersiniz, ancak bu tamsayı hiçbir yerde kullanamayacağınız ihtimali vardır.

Ayrıca, @BinaryZebra tarafından belirtildiği gibi, çeşitli printfuygulamalarda (bash, ksh93, yash, GNU, zsh, dash değil), yerel ayardan etkilenebilir (ondalık ayırıcı olabilir .veya olabilir ,).

Bu nedenle, kayan noktalarınız her zaman ondalık ayırıcı olarak dönemle ifade edilirse ve printfkomut dosyasını çağıran kullanıcının yerel ayarına bakılmaksızın bunun gibi değerlendirilmesini istiyorsanız, yerel ayarı C'ye sabitlemeniz gerekir:

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

İle yashşunları da yapabilirsiniz:

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

(aşağıya bakınız).

POSIX

printf "%.0f\n" 1.1

POSIX %ftarafından desteklenmesi gerekmediğinden POSIX değildir.

POSIXly şunları yapabilirsiniz:

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

Bir yerel ayar etkilenmez O (virgül bir ondalık ayırıcı olamaz awkzaten sözdiziminde özel bir karakter beri orada ( print 1,2aynı print 1, 2üzere iki argüman geçmek print)

zsh

In zsh(ondalık ayırıcı daima dönem) olan kayar nokta aritmetik destekler (), sahip rint()bir float olarak (in sever yakın tamsayı vermek için matematik fonksiyonu C) ve int()(filmindeki gibi bir şamandıra gelen size bir tamsayı vermek awk). Yani yapabilirsin:

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

Veya:

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

Bununla birlikte, doubles çok büyük sayıları temsil etse de, tam sayılar çok daha sınırlıdır.

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

ksh93

ksh93, kayan nokta aritmetiğini destekleyen ilk Bourne benzeri kabuktur. ksh93 komutları yalnızca yerleşik komutlar olduğunda, bir boru kullanmadan veya çatal kullanarak komut değiştirmeyi optimize eder. Yani

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

çatal değil. Veya daha da iyisi:

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

Bu da ya çatal değil aynı zamanda sahte bir deniz kabuğu ortamı yaratma zorluğuna da yol açmaz.

Ayrıca şunları da yapabilirsiniz:

i=$((rint(f)))

Ancak dikkat:

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

Ayrıca şunları da yapabilirsiniz:

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

Ancak bunun için zsh:

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

ksh93Kayan nokta aritmetiğinin yerel ayardaki ondalık ayırıcı ayarını onurlandırdığına dikkat edin ( ,aksi halde bir matematik işleci olsa da ( $((1,2))Fransızca / Almanca ... yerel ayarında 6/5 olur ve aynı $((1, 2)), İngilizce ayarında 2 olur) .

yash

yash, kayan nokta aritmetiğini de destekler, ancak ksh93/ zsh's gibi matematik işlevlerine sahip değildir rint(). Örneğin bir ikiliyi veya işlecini kullanarak bir sayıyı tamsayıya dönüştürebilirsiniz (aynı zamanda içinde çalışır zshancak içinde değildir ksh93). Ancak ondalık parçayı kısalttığına, size en yakın tamsayıya sahip olmadığını unutmayın:

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

yash yerelin çıktıdaki ondalık ayırıcısını onurlandırır, ancak aritmetik ifadelerinde kayan nokta değişmez sabitleri için değil, sürprizlere neden olabilir:

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

Komut dosyalarınızda, süreyi kullanan kayan nokta sabitlerini kullanabilmeniz ve başka yerlerde çalışmayı bırakacağından endişe etmenize gerek kalmaması, ancak yine de kullanıcı tarafından belirtilen sayılarla başa çıkabilmeniz için iyi bir yöntemdir. Yapmayı hatırladığın gibi:

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%.*}Mac'teki basım (sürüm 3.2.57 (1)-sürüm) komut dosyasında çok iyi çalıştı (sürüm 10.13.4 (17E199)).
TelamonAegisthus

İlk formülasyon GNU bash 4.4.19'da başarısız oluyor, örneğin: "` $ $ echo $ maksimum 32.800 $ printf -v int% .0f "$ maximum" bash: printf: 32.800: geçersiz sayı
Luís de Sousa

@ LuísdeSousa, yerel ayarınız büyük olasılıkla ,ondalık sayı tabanına sahiptir . Hakkında bölüme bakınLC_ALL=C
Stéphane Chazelas

Bu cevabın farklı bir soruyu cevapladığına dikkat edin: Noktadan sonra rakamlar nasıl sorulursa değil nasıl kaldırılır: tamsayıya yüzdürmenin doğru yolu nedir ?
Isaac

Bu yöntem herhangi bir sayıda bitlik yüzgeçlere uygulanmalı mıdır? Float 23 bit veya 128 bit mantis için aynı mı olmalı?
Isaac

21

bc - Keyfi bir hassas hesap makinesi dili

int (float) şöyle görünmeli:

$ echo "$float/1" | bc 
1234

Daha iyi yuvarlamak için şunu kullanın:

$ echo "($float+0.5)/1" | bc 

Örnek:

$ float=1.49
$ echo "($float+0.5)/1" | bc 
1
$ float=1.50
$ echo "($float+0.5)/1" | bc 
2

4
Ama float=-2; echo "($float+0.5)/1" | bcverir -1.
Stéphane Chazelas 12:14

2
Buna yuvarlak + inf yönünde denir. Bu, yuvarlamanın dört yolundan biridir .

5

Gönderilen önceki cevap neredeyse doğruydu: "Yapabilecekleriniz:

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

Fakat bu size en yakın tamsayıyı vermek yerine kesirli kısmı ortadan kaldıracak ve örneğin 1.2e9 ya da .12 gibi $ float değerleri için işe yaramayacaktı .... ”

Sadece kullan ${float%%.*}.

echo ${float%%.*}
1

3

Çok basit bir hack olanı

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

Örnek çıktı

$ float_to_int 32.333
32
$ float_to_int 32
32

Kullanımı mümkün olurdu sed, ama cutçözüm daha basittir. IMHO'yu gömülü hedeflerde (hala oldukça yaygın olanlardan ) daha fazla tercih edebiliyorum sedveya tercih ediyorum . cutawkbusyboxbc
pevik

0

İlgili:

  1. nasıl tamsayıya yapmak şamandıra awk 'de ,
  2. Kayan nokta sayıları kabukta nasıl yuvarlanır?

Tamamlayan @ Stéphane Chazelas awk cevap:

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] )}' "$@";
}

awks'imde, ikincisi sıfıra doğru yuvarlanır, eksi sonsuzluğa değil ("taban" olarak alınabilir). Ayrıca, ilk turlar eşit sayılara yarıya iner. Bash'in yaptıklarından farklı bir şey olup olmadığından emin değilim printfya da yerleşkeyi tercih etmenin başka bir nedeni var mı printf?
ilkkachu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.