Come convertire il numero in virgola mobile in intero?


47

È questo il modo giusto per fare la conversione da float a intero in bash? C'è qualche altro metodo?

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

5
Cos'è giusto"?
Joseph R.,

Voglio solo assicurarmi ... è giusto o c'è di meglio?
Rahul Patil,

2
%.0farrotonderà per eccesso o per difetto. È questo che vuoi? Puoi usare printf "%d\n" "$@" 2>/dev/nullper tagliare la frazione.
ott--

Risposte:


70

bash

In bash, probabilmente è buono come si arriva. Questo utilizza un shell incorporato. Se hai bisogno del risultato in una variabile, puoi usare la sostituzione dei comandi o lo bashspecifico (anche se ora supportato anche da zsh):

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

Potresti fare:

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

Ma ciò eliminerebbe la parte frazionaria invece di darti il ​​numero intero più vicino e che non funzionerebbe per valori di $floatlike 1.2e9o .12per esempio.

Si noti inoltre le possibili limitazioni dovute alla rappresentazione interna dei galleggianti:

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

Ottieni un numero intero, ma è probabile che non sarai in grado di utilizzare quel numero intero da nessuna parte.

Inoltre, come notato da @BinaryZebra, in diverse printfimplementazioni (bash, ksh93, yash, non GNU, zsh, dash), è influenzato dalla locale (il separatore decimale che può essere .o ,).

Quindi, se i tuoi float sono sempre espressi con il punto come separatore decimale e vuoi che sia trattato come tale printfindipendentemente dalle impostazioni locali dell'utente che invoca il tuo script, dovrai correggere le impostazioni locali su C:

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

Con yash, puoi anche fare:

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

(vedi sotto).

POSIX

printf "%.0f\n" 1.1

non è POSIX in quanto %fnon è necessario che sia supportato da POSIX.

POSIXly, puoi fare:

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

Quello non è influenzato dalle impostazioni locali (la virgola non può essere un separatore decimale in awkquanto è già un carattere speciale nella sintassi lì ( print 1,2, come print 1, 2nel passare due argomenti a print)

zsh

In zsh(che supporta l'aritmetica in virgola mobile (il separatore decimale è sempre il punto)), hai la rint()funzione matematica per darti il ​​numero intero più vicino come float (come in C) e int()darti un numero intero da un float (come in awk). Quindi puoi fare:

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

O:

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

Tuttavia nota che mentre doubles può rappresentare numeri molto grandi, gli interi sono molto più limitati.

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

ksh93

ksh93 è stata la prima shell tipo Bourne a supportare l'aritmetica in virgola mobile. ksh93 ottimizza la sostituzione dei comandi non usando una pipe o biforcando quando i comandi sono solo comandi incorporati. Così

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

non si biforca. O ancora meglio:

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

che non si biforca, ma che non crea problemi con la creazione di un ambiente subshell falso.

Puoi anche fare:

i=$((rint(f)))

Ma attenzione a:

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

Puoi anche fare:

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

Ma come per zsh:

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

Attenzione che l' ksh93aritmetica in virgola mobile rispetta l'impostazione del separatore decimale nella locale (anche se ,altrimenti $((1,2))sarebbe un operatore matematico ( sarebbe 6/5 in una locale francese / tedesca ... e lo stesso di $((1, 2))2 in una locale inglese) .

Yash

yash supporta anche l'aritmetica in virgola mobile ma non ha funzioni matematiche come ksh93/ zsh's rint(). Puoi convertire un numero in numero intero usando ad esempio il binario o l' operatore (funziona anche in zshma non in ksh93). Si noti tuttavia che tronca la parte decimale, non ti dà il numero intero più vicino:

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

yash onora il separatore decimale del locale sull'output, ma non per le costanti letterali in virgola mobile nelle sue espressioni aritmetiche, che possono causare sorprese:

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

È buono in un certo senso in quanto puoi usare costanti in virgola mobile nei tuoi script che usano il punto e non devi preoccuparti che smetterà di funzionare in altre versioni locali, ma sarà comunque in grado di gestire i numeri espressi dall'utente per tutto il tempo come ti ricordi di fare:

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%.*}ha funzionato molto bene con il mio script bash (versione 3.2.57 (1)-release) su un Mac (versione 10.13.4 (17E199)).
Telamon Aegisthus,

La tua prima formulazione fallisce in GNU bash 4.4.19, ad esempio: `` $ echo $ massimo 32.800 $ printf -v int% .0f "$ massimo" bash: printf: 32.800: numero non valido ``
Luís de Sousa

@ LuísdeSousa, il tuo locale probabilmente ha ,come radice decimale. Vedi la sezione suLC_ALL=C
Stéphane Chazelas il

Nota che questa risposta risponde a una domanda diversa: come rimuovere le cifre dopo il punto e non quello che è stato chiesto: qual è il modo giusto di fare il float su intero .
Isaac,

Questo metodo dovrebbe essere applicato ai float di qualsiasi numero di bit? Dovrebbe essere lo stesso per un float di 23 bit o 128 bit di mantissa?
Isaac,

21

bc - Un linguaggio di calcolatrice di precisione arbitrario

int (float) dovrebbe apparire come:

$ echo "$float/1" | bc 
1234

Per arrotondare meglio usare questo:

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

Esempio:

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

4
Ma float=-2; echo "($float+0.5)/1" | bc-1.
Stéphane Chazelas,

2
Questo si chiama round verso + inf. Questo è uno dei quattro modi possibili per arrotondare .

5

La risposta precedente inviata era quasi corretta: "Potresti fare:

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

Ma questo eliminerebbe la parte frazionaria invece di darti il ​​numero intero più vicino e non funzionerebbe per valori di $ float come 1.2e9 o .12 per esempio .... "

Basta usare ${float%%.*}.

echo ${float%%.*}
1

3

Uno hacky molto semplice è

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

Uscita campione

$ float_to_int 32.333
32
$ float_to_int 32
32

Sarebbe possibile usare sed, ma la cutsoluzione è più semplice. Preferisco sedo cutpoiché sono IMHO più disponibili su target integrati rispetto a awk(che è ancora abbastanza comune tramite busyboxdi bc).
pevik,

0

Relazionato:

  1. come rendere float a numero intero in awk ,
  2. Come arrotondare i numeri in virgola mobile nella shell?

Complementare @ Stéphane Chazelas awk risposta:

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

sul mio awk, quest'ultimo ruota verso lo zero, non a meno infinito (poiché "pavimento" può essere inteso nel senso). Inoltre, i primi round si dimezzano a numeri pari. Non sono sicuro che sia diverso da quello che fa Bash printf, o se ci sono altri motivi per preferire awk rispetto al builtin printf.
ilkkachu,
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.