Como converter um número de ponto flutuante em inteiro?

41

É essa a maneira correta de fazer float para integer no bash, ou existe algum outro método?

flotToint() {
    printf "%.0f\n" "$@"
}
    
por Rahul Patil 06.09.2013 / 22:36

5 respostas

61

bash

Em bash , provavelmente é o melhor possível. Isso usa um shell embutido. Se você precisar do resultado em uma variável, poderá usar a substituição de comando ou o bash específico (embora agora também seja suportado por zsh ):

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

Você poderia fazer:

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

Mas isso removeria a parte fracionária em vez de fornecer o inteiro mais próximo e isso não funcionaria para valores de $float como 1.2e9 ou .12 , por exemplo.

Observe também as possíveis limitações devido à representação interna dos floats:

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

Você obtém um número inteiro, mas é provável que não seja possível usar esse número inteiro em lugar algum.

Além disso, conforme observado por @BinaryZebra, em várias implementações de printf (bash, ksh93, yash, não GNU, zsh, dash), ele é afetado pela localidade (o separador decimal que pode ser . ou , ).

Assim, se os seus carros alegóricos forem sempre expressos com o período como o separador decimal e você quiser que ele seja tratado como tal por printf , independentemente da localidade do usuário invocando seu script, será necessário corrigir a localidade para C:

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

Com yash , você também pode fazer:

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

(veja abaixo).

POSIX

printf "%.0f\n" 1.1

não é POSIX, pois %f não precisa ser suportado pelo POSIX.

POSIXly, você pode fazer:

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

Esse não é afetado pela localidade (a vírgula não pode ser um separador decimal em awk , já que ele já é um caractere especial na sintaxe ( print 1,2 , o mesmo que print 1, 2 para passar dois argumentos para print )

zsh

Em zsh (que suporta aritmética de ponto flutuante (o separador decimal é sempre o período)), você tem a função rint() math para fornecer o inteiro mais próximo como float (como em C ) e int() para dar um inteiro a partir de um float (como em awk ). Então você pode fazer:

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

Ou:

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

No entanto, observe que, embora double s possa representar números muito grandes, os números inteiros são muito mais limitados.

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

ksh93

O ksh93 foi o primeiro shell tipo Bourne a suportar a aritmética de ponto flutuante. O ksh93 otimiza a substituição de comandos ao não usar um pipe ou bifurcação quando os comandos são apenas comandos incorporados. Então

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

não bifurca. Ou melhor ainda:

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

que não é bifurcado, mas também não se dá ao trabalho de criar um ambiente subnível falso.

Você também pode fazer:

i=$((rint(f)))

Mas cuidado com:

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

Você também pode fazer:

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

Mas gosto de zsh :

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

Tenha em atenção que ksh93 aritmética de ponto flutuante honra a definição do separador decimal na região (mesmo que , seja um operador matemático ( $((1,2)) seria 6/5 numa região francesa / alemã ... e o mesmo que $((1, 2)) , ou seja, 2 em uma localidade inglesa).

yash

yash também suporta aritmética de ponto flutuante, mas não possui funções matemáticas como ksh93 / zsh ' rint() . Você pode converter um número para inteiro usando o operador binário ou , por exemplo (também funciona em zsh , mas não em ksh93 ). Note, no entanto, que trunca a parte decimal, não lhe dá o inteiro mais próximo:

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

yash homenageia o separador decimal da localidade na saída, mas não as constantes literais de ponto flutuante em suas expressões aritméticas, o que pode causar surpresas:

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

É bom de uma forma em que você pode usar constantes de ponto flutuante em seus scripts que usam o período e não precisa se preocupar que ele parará de funcionar em outros locais, mas ainda será capaz de lidar com os números expressos pelo usuário, desde que você se lembre de fazer:

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
    
por 07.09.2013 / 09:05
18

bc - Uma linguagem calculadora de precisão arbitrária

int (float) deve se parecer com:

$ echo "$float/1" | bc 
1234

Para arredondar melhor, use isso:

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

Exemplo:

$ float=1.49
$ echo "($float+0.5)/1" | bc 
1
$ float=1.50
$ echo "($float+0.5)/1" | bc 
2
    
por 08.09.2013 / 11:44
4

instale o programa beep com:

aptitude install beep
or
apt-get install beep

e ouça a função sine assim:

1a. define a função no bash:

$ function sinus () 
{ 
    a=$1;
    b=$2;
    n=$3;
    f=$4;
    d=$(echo "scale=$f; ($b-$a)/$n"|bc);
    for m in $(seq 0 $n);
    do
        x=$(echo "scale=$f;$a+$m*$d"|bc);
        y=$(echo "scale=$f;s($x)"|bc -l);
        #printf "%0.${f}f\t" "$x";
        #printf "%0.${f}f\n" "$y";
        z=$(echo "scale=$f; 2300+2000*$y" | bc);
        beep -f "$z" -l 2 -d 0;
    done | column -t
}

2a. Chame essa função assim:

$ sinus 1 20 300 4

Nota : 'sinus abnf' significa ouvir o som de seno (em radianos) de a para b dividindo o intervalo em < em> n + 1 partes iguais usando f decimais. Há mais coisas do que o necessário na função, porque você pode usá-las para plotar ou qualquer outra coisa. Você também pode escrever ler um b n f em vez das inicializações.

Além disso, muito engraçado, esmagando os ciclos na função sine cada vez mais até que o programa não aguente:

1b. define:

$ function sinus-inv () 
{ 
    a=$1; b=$2; n=$3; f=$4;
    d=$(echo "scale=$f; ($b-$a)/$n"|bc);
    for m in $(seq 1 $n);
    do
        x=$(echo "scale=$f;$a+$m*$d"|bc);
        y=$(echo "scale=$f;s(8000/($b-$x))"|bc -l);
        z=$(echo "scale=$f; 2300+2000*$y" | bc);
        beep -f "$z" -l 1 -d 0;
    done;
}

2b. Exemplo de chamada:

$ sinus-inv 1 100 1000 8
    
por 26.02.2014 / 19:36
3

A resposta anterior enviada estava quase correta: "Você poderia fazer:

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

Mas isso removeria a parte fracionária em vez de fornecer o número inteiro mais próximo e isso não funcionaria para valores de $ float como 1.2e9 ou .12, por exemplo ... "

Use apenas ${float%%.*} .

echo ${float%%.*}
1
    
por 12.10.2016 / 19:24
2

Um hacky muito simples é

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

Exemplo de saída

$ float_to_int 32.333
32
$ float_to_int 32
32
    
por 13.10.2016 / 05:09