Maneira mais segura de converter float para inteiro em python?

215

O módulo de matemática do Python contém funções úteis como floor& ceil. Essas funções usam um número de ponto flutuante e retornam o número inteiro mais próximo abaixo ou acima dele. No entanto, essas funções retornam a resposta como um número de ponto flutuante. Por exemplo:

import math
f=math.floor(2.3)

Agora fretorna:

2.0

Qual é a maneira mais segura de obter um número inteiro desse float, sem correr o risco de erros de arredondamento (por exemplo, se o float é o equivalente a 1.99999) ou talvez eu deva usar outra função completamente?

Boaz
fonte
7
math.floor retorna um ponto flutuante na v2.6 , mas retorna um número inteiro na v3 . Neste ponto (quase seis anos após o PO), esse problema pode aparecer raramente.
sancho.s ReinstateMonicaCellio
no entanto, numpy ainda retorna float, então a pergunta é válida.
Vincenzooo

Respostas:

178

Todos os números inteiros que podem ser representados por números de ponto flutuante têm uma representação exata. Então você pode usar com segurança intno resultado. Representações inexatas ocorrem apenas se você estiver tentando representar um número racional com um denominador que não é um poder de dois.

Que isso funcione não é nada trivial! É uma propriedade da representação de ponto flutuante IEEE que int∘floor = ⌊⋅⌋ se a magnitude dos números em questão for pequena o suficiente, mas diferentes representações são possíveis onde int (floor (2.3)) pode ser 1.

Para citar a Wikipedia ,

Qualquer número inteiro com valor absoluto menor ou igual a 2 24 pode ser representado exatamente no formato de precisão única, e qualquer número inteiro com valor absoluto menor ou igual a 2 53 pode ser representado exatamente no formato de precisão dupla.

Philipp
fonte
8
+1 por aprofundar um pouco. Você também pode dar uma breve explicação sobre o porquê: en.wikipedia.org/wiki/Floating_point : D
Gordon Gustafson
No Python 2, um "int" é o mesmo que um C "int". No Python 3, parece que não há limite para o tamanho de um "int, stackoverflow.com/questions/13795758/… . O significado de" int "também depende do sistema operacional e do hardware subjacente. Consulte a en.wikipedia. org / wiki / 64-bit_computing # 64-bit_data_models Se você estiver programando com a C-API, python 3, precisará ter muito cuidado com a definição de long e size_t na sua plataforma. docs.python.org/3 /c-api/long.html
Juan
112

O uso int(your non integer number)vai pregar.

print int(2.3) # "2"
print int(math.sqrt(5)) # "2"
srodriguex
fonte
4
Isso não vai funcionar para números negativos: floorrodadas para baixo enquanto introdadas para a 0.
jochen
1
@jochen Testei int(-2.3)na distribuição Python Canopy 2.7.6 e obtive -2como esperado. Os números inteiros podem ser negativos, da mesma forma na definição formal de matemática.
22414 srvriguex
5
Eu concordo, int(-2.3)-2como você diz, porque se aproxima 0, ou seja , neste caso. Por outro lado, a pergunta original usada math.floor, que sempre arredonda para baixo: math.floor(-2.3)-3.0.
jochen
1
Isso não é realmente um problema. OP só quer um número inteiro fora do resultado de math.floor, e esta resposta mostra como converter um número flutuante em um número inteiro. Tome a bóia de math.floore canalizá-lo através int, problema resolvido:int(math.floor(2.3))
JMTyler
4
Você leu a pergunta? Ele está ciente da função int () , mas perguntou se você pode ter problemas com a 1.9999 em vez da 2.0. Sua resposta não chega nem perto de uma resposta, você perdeu o ponto ... #
Mayou36
48

Você poderia usar a função round. Se você não usar um segundo parâmetro (número de dígitos significativos), acho que obterá o comportamento desejado.

Saída IDLE.

>>> round(2.99999999999)
3
>>> round(2.6)
3
>>> round(2.5)
3
>>> round(2.4)
2
Wade73
fonte
28
roundretorna um número flutuante também, pelo menos no Python 2.6.
Philipp
8
No Python 3.1.2, round retorna um int.
Robert2 de
2
De fato, ambos rounde floorretornam números inteiros no Python 3.x. Então, suponho que a pergunta se refira ao Python 2.x.
Philipp
4
então talvez int(round(2.65))?
teewuane
1
por que round(6.5)dá 6? Parece meio que ceil()flutuar quando há um 5 imediato (ou superior a 9) após o decimal em todos os outros casos. Por que isso não está funcionando neste caso? ou qualquer outro caso, quando as extremidades número com um seis e há a 5 logo após o decimal ...
candh
43

Combinando dois dos resultados anteriores, temos:

int(round(some_float))

Isso converte um número flutuante em um número inteiro de maneira bastante confiável.

user3763109
fonte
O que acontece se você tentar arredondar um flutuador muito longo? Isso ao menos gerará uma exceção?
Agostino
@Agostino O que você quer dizer com "bóia muito longa"?
kralyk
@kralyk Quero dizer um floatrepresentando um número maior do que o normal intpode conter. No Python 2, existem floatvalores que você só pode representar usando a long(após o arredondamento)?
Agostino
@kralyk você quer dizer, depois da rodada? Então, lançá-los para int criar uma exceção ou apenas truncá-los?
Agostino
@Agostino Não, a int()função produz tanto um intou longcom base no que é necessário ...
kralyk
18

Que isso funcione não é nada trivial! É uma propriedade da representação de ponto flutuante IEEE que int∘floor = ⌊⋅⌋ se a magnitude dos números em questão for pequena o suficiente, mas diferentes representações são possíveis onde int (floor (2.3)) pode ser 1.

Este post explica por que funciona nesse intervalo .

Em um dobro, você pode representar números inteiros de 32 bits sem problemas. Não pode haver problemas de arredondamento. Mais precisamente, as dobras podem representar todos os números inteiros entre 2 53 e -2 53 inclusive .

Breve explicação : Um duplo pode armazenar até 53 dígitos binários. Quando você precisar de mais, o número é preenchido com zeros à direita.

Daqui resulta que 53 ones é o maior número que pode ser armazenado sem preenchimento. Naturalmente, todos os números (inteiros) que exigem menos dígitos podem ser armazenados com precisão.

Adicionar um a 111 (omitido) 111 (53 unidades) produz 100 ... 000 (53 zeros). Como sabemos, podemos armazenar 53 dígitos, o que resulta no preenchimento zero mais à direita.

É daí que vem o 2 53 .


Mais detalhes: Precisamos considerar como o ponto flutuante IEEE-754 funciona.

  1 bit    11 / 8     52 / 23      # bits double/single precision
[ sign |  exponent | mantissa ]

O número é calculado da seguinte maneira (excluindo casos especiais que são irrelevantes aqui):

-1 sinal × 1.mantissa × 2 expoente - viés

onde viés = 2 expoente - 1 - 1 , ou seja, 1023 e 127 para precisão dupla / única, respectivamente.

Sabendo que multiplicar por 2 X simplesmente muda todos os bits X para a esquerda, é fácil ver que qualquer número inteiro deve ter todos os bits na mantissa que terminam à direita do ponto decimal em zero.

Qualquer número inteiro, exceto zero, tem o seguinte formato em binário:

1x ... x onde os x -es representam os bits à direita do MSB (bit mais significativo).

Como excluímos zero, sempre haverá um MSB, e é por isso que não é armazenado. Para armazenar o número inteiro, devemos trazê-lo para a forma acima mencionada: -1 sinal × 1.mantissa × 2 expoente - viés .

É o mesmo que mudar os bits sobre o ponto decimal até que haja apenas o MSB à esquerda do MSB. Todos os bits à direita do ponto decimal são armazenados na mantissa.

A partir disso, podemos ver que podemos armazenar no máximo 52 dígitos binários além do MSB.

Segue-se que o número mais alto em que todos os bits são armazenados explicitamente é

111(omitted)111.   that's 53 ones (52 + implicit 1) in the case of doubles.

Para isso, precisamos definir o expoente, de modo que o ponto decimal seja deslocado em 52 casas. Se aumentarmos o expoente em um, não saberemos o dígito da direita para a esquerda após o ponto decimal.

111(omitted)111x.

Por convenção, é 0. Definindo a mantissa inteira como zero, recebemos o seguinte número:

100(omitted)00x. = 100(omitted)000.

Isso é 1 seguido de 53 zeros, 52 armazenados e 1 adicionado devido ao expoente.

Representa 2 53 , que marca o limite (negativo e positivo) entre o qual podemos representar com precisão todos os números inteiros. Se quiséssemos adicionar um a 2 53 , teríamos que definir o zero implícito (indicado por x) para um, mas isso é impossível.

phant0m
fonte
8

math.floorsempre retornará um número inteiro e, portanto int(math.floor(some_float)), nunca introduzirá erros de arredondamento.

O erro de arredondamento já pode ter sido introduzido math.floor(some_large_float), ou mesmo ao armazenar um grande número em um float em primeiro lugar. (Números grandes podem perder precisão quando armazenados em carros alegóricos.)


fonte
7
De: docs.python.org/2/library/math.html - math.floor (x) - Retorna o piso de x como um flutuador, o maior valor inteiro menor ou igual a x.
Bill Rosmus
Por que você precisa chamar math.floor quando int já faz a mesma coisa?
Alex
1
@ Alex: inte floorretorne valores diferentes para números negativos, é claro.
8

Se você precisar converter uma seqüência de caracteres flutuante em um int, poderá usar esse método.

Exemplo: '38.0'para38

Para converter isso em um int, você pode convertê-lo como um float e depois em um int. Isso também funcionará para cadeias flutuantes ou cadeias inteiras.

>>> int(float('38.0'))
38
>>> int(float('38'))
38

Nota : Isso retira qualquer número após o decimal.

>>> int(float('38.2'))
38
brandonbanks
fonte
1

Outro exemplo de código para converter um real / flutuante em um número inteiro usando variáveis. "vel" é um número real / flutuante e convertido para o próximo mais alto INTEGER, "newvel".

import arcpy.math, os, sys, arcpy.da
.
.
with arcpy.da.SearchCursor(densifybkp,[floseg,vel,Length]) as cursor:
 for row in cursor:
    curvel = float(row[1])
    newvel = int(math.ceil(curvel))
mrichey56
fonte
0

Como você está pedindo a maneira 'mais segura', fornecerei outra resposta que não seja a principal.

Uma maneira fácil de garantir que você não perca nenhuma precisão é verificar se os valores serão iguais após a conversão.

if int(some_value) == some_value:
     some_value = int(some_value)

Se o float for 1,0, por exemplo, 1,0 é igual a 1. Portanto, a conversão para int será executada. E se o float for 1.1, int (1.1) equivale a 1 e 1.1! = 1. Portanto, o valor permanecerá um float e você não perderá nenhuma precisão.

Troy H
fonte
0

df ['Nome_da_coluna'] = df ['Nome_da_coluna']. astype (int)

Niruttara
fonte
Uma explicação para sua resposta é muito útil.
bibliophilsagar