¿La forma más segura de convertir flotante a entero en Python?

215

El módulo matemático de Python contiene funciones prácticas como floor& ceil. Estas funciones toman un número de coma flotante y devuelven el entero más cercano debajo o encima de él. Sin embargo, estas funciones devuelven la respuesta como un número de coma flotante. Por ejemplo:

import math
f=math.floor(2.3)

Ahora fvuelve:

2.0

¿Cuál es la forma más segura de obtener un número entero de este flotante, sin correr el riesgo de errores de redondeo (por ejemplo, si el flotante es el equivalente a 1.99999) o tal vez debería usar otra función por completo?

Booz
fuente
77
math.floor devuelve un flotante en v2.6 , pero devuelve un entero en v3 . En este punto (casi seis años después del OP), este problema podría aparecer raramente.
sancho.s ReinstateMonicaCellio
sin embargo, numpy aún devuelve flotante, por lo que la pregunta es válida.
Vincenzooo

Respuestas:

178

Todos los enteros que se pueden representar con números de coma flotante tienen una representación exacta. Por lo tanto, puede usarlo con seguridad inten el resultado. Las representaciones inexactas ocurren solo si está tratando de representar un número racional con un denominador que no es una potencia de dos.

¡Que esto funcione no es trivial en absoluto! Es una propiedad de la representación de coma flotante IEEE que int∘floor = ⌊⋅⌋ si la magnitud de los números en cuestión es lo suficientemente pequeña, pero son posibles diferentes representaciones donde int (floor (2.3)) podría ser 1.

Para citar de Wikipedia ,

Cualquier número entero con un valor absoluto menor o igual a 2 24 puede representarse exactamente en el formato de precisión simple, y cualquier número entero con un valor absoluto menor o igual a 2 53 puede representarse exactamente en el formato de doble precisión.

Philipp
fuente
8
+1 por ir un poco más profundo. También puede dar una breve explicación de por qué: en.wikipedia.org/wiki/Floating_point : D
Gordon Gustafson
En Python 2, un "int" es lo mismo que un C "int". En Python 3, parece que no hay límite para el tamaño de un "int, stackoverflow.com/questions/13795758/… . El significado de" int "también depende del sistema operativo y del hardware subyacente. Consulte en.wikipedia. org / wiki / 64-bit_computing # 64-bit_data_models . Si está programando con C-API, python 3, debe tener mucho cuidado con la definición de long y size_t en su plataforma. docs.python.org/3 /c-api/long.html
Juan
112

El uso int(your non integer number)lo clavará.

print int(2.3) # "2"
print int(math.sqrt(5)) # "2"
srodriguex
fuente
44
Esto no funcionará para números negativos: floorredondea hacia abajo mientras que intredondea hacia 0.
jochen
1
@jochen Probé int(-2.3)en Python Distribution Canopy 2.7.6 y obtuve -2lo esperado. Los números enteros pueden ser negativos, de la misma manera en la definición matemática formal.
srodriguex
55
Estoy de acuerdo, int(-2.3)da -2como dices, porque se redondea hacia 0, es decir , hacia arriba en este caso. Por el contrario, la pregunta original utilizada math.floor, que siempre se redondea: math.floor(-2.3)da -3.0.
jochen
1
Eso no es realmente un problema. OP solo quiere un entero fuera del resultado de math.floor, y esta respuesta muestra cómo convertir un flotante en un entero. Tome el flotador math.floory intint(math.floor(2.3))
páselo
44
¿Leíste la pregunta? Es consciente de la función int () , pero le ha preguntado si puede tener problemas con 1.9999 en lugar de 2.0. Su respuesta ni siquiera está cerca de una respuesta en absoluto, se perdió todo el punto ...
Mayou36
48

Podrías usar la función redonda. Si no utiliza ningún segundo parámetro (número de dígitos significativos), creo que obtendrá el comportamiento que desea.

Salida inactiva.

>>> round(2.99999999999)
3
>>> round(2.6)
3
>>> round(2.5)
3
>>> round(2.4)
2
Wade73
fuente
28
roundtambién devuelve un número flotante, al menos en Python 2.6.
Philipp
8
En Python 3.1.2, round devuelve un int.
robert
2
De hecho, ambos roundy los floorenteros devueltos en Python 3.x. Así que supongo que la pregunta se refiere a Python 2.x.
Philipp
44
así que tal int(round(2.65))?
teewuane
1
¿Por qué round(6.5)da 6? Parece un poco ceil()flotante cuando hay un 5 inmediato (o mayor hasta 9) después del decimal en todos los demás casos. ¿Por qué esto no funciona en este caso? o cualquier otro caso cuando el número termina con un seis y hay un 5 justo después del decimal ...
candh
43

Combinando dos de los resultados anteriores, tenemos:

int(round(some_float))

Esto convierte un flotador en un entero de manera bastante confiable.

usuario3763109
fuente
¿Qué sucede si intentas redondear una carroza muy larga? ¿Esto al menos generará una excepción?
Agostino
@ Agostino ¿Qué quieres decir con "flotación muy larga"?
kralyk
@kralyk me refiero a una floatrepresentación de un número más grande de lo que intpuede contener un normal . En Python 2, ¿hay floatvalores que solo puede representar usando un long(después del redondeo)?
Agostino
@kralyk quieres decir, después de la ronda? Entonces, ¿lanzarlos a int generaría una excepción, o simplemente truncarlos?
Agostino
@Agostino No, la int()función produce una into una longfunción de lo que se necesita ...
kralyk
18

¡Que esto funcione no es trivial en absoluto! Es una propiedad de la representación de coma flotante IEEE que int∘floor = ⌊⋅⌋ si la magnitud de los números en cuestión es lo suficientemente pequeña, pero son posibles diferentes representaciones donde int (floor (2.3)) podría ser 1.

Esta publicación explica por qué funciona en ese rango .

En un doble, puede representar enteros de 32 bits sin ningún problema. No puede haber problemas de redondeo. Más precisamente, los dobles pueden representar todos los enteros entre 2 53 y -2 53 inclusive .

Breve explicación : un doble puede almacenar hasta 53 dígitos binarios. Cuando necesita más, el número se rellena con ceros a la derecha.

Se deduce que 53 unidades es el número más grande que se puede almacenar sin relleno. Naturalmente, todos los números (enteros) que requieren menos dígitos se pueden almacenar con precisión.

Agregar uno a 111 (omitido) 111 (53 unidades) produce 100 ... 000, (53 ceros). Como sabemos, podemos almacenar 53 dígitos, lo que hace que el margen cero más a la derecha.

De aquí viene el 2 53 .


Más detalles: debemos considerar cómo funciona el punto flotante IEEE-754.

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

El número se calcula de la siguiente manera (excluyendo casos especiales que son irrelevantes aquí):

-1 signo × 1.mantissa × 2 exponente - sesgo

donde sesgo = 2 exponente - 1 - 1 , es decir, 1023 y 127 para precisión doble / simple respectivamente.

Sabiendo que multiplicar por 2 X simplemente cambia todos los bits X lugares a la izquierda, es fácil ver que cualquier número entero debe tener todos los bits en la mantisa que terminan a la derecha del punto decimal a cero.

Cualquier número entero excepto cero tiene la siguiente forma en binario:

1x ... x donde las x -es representan los bits a la derecha del MSB (bit más significativo).

Debido a que excluimos cero, siempre habrá un MSB que sea uno, razón por la cual no se almacena. Para almacenar el entero, debemos llevarlo a la forma mencionada anteriormente: -1 signo × 1.mantissa × 2 exponente - sesgo .

Eso dice lo mismo que desplazar los bits sobre el punto decimal hasta que solo esté el MSB hacia la izquierda del MSB. Todos los bits a la derecha del punto decimal se almacenan en la mantisa.

A partir de esto, podemos ver que podemos almacenar como máximo 52 dígitos binarios aparte del MSB.

Se deduce que el número más alto donde todos los bits se almacenan explícitamente es

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

Para esto, necesitamos establecer el exponente, de modo que el punto decimal se desplace 52 lugares. Si tuviéramos que aumentar el exponente en uno, no podríamos conocer el dígito de derecha a izquierda después del punto decimal.

111(omitted)111x.

Por convención, es 0. Al establecer toda la mantisa en cero, recibimos el siguiente número:

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

Es un 1 seguido de 53 ceros, 52 almacenados y 1 agregado debido al exponente.

Representa 2 53 , que marca el límite (tanto negativo como positivo) entre el que podemos representar con precisión todos los enteros. Si quisiéramos agregar uno a 2 53 , tendríamos que establecer el cero implícito (indicado por el x) en uno, pero eso es imposible.

phant0m
fuente
8

math.floorsiempre devolverá un número entero y, por int(math.floor(some_float))lo tanto , nunca introducirá errores de redondeo.

Sin math.floor(some_large_float)embargo, el error de redondeo ya podría haberse introducido , o incluso cuando se almacena un gran número en un flotante en primer lugar. (Grandes números pueden perder precisión cuando se almacenan en flotadores).


fuente
77
De: docs.python.org/2/library/math.html - math.floor (x) - Devuelve el piso de x como flotante, el mayor valor entero menor o igual que x.
Bill Rosmus
¿Por qué necesita llamar a math.floor cuando int ya hace lo mismo?
Alex
1
@Alex: inty floordevuelve diferentes valores para números negativos, por supuesto.
8

Si necesita convertir una cadena flotante a un int, puede usar este método.

Ejemplo: '38.0'a38

Para convertir esto en un int, puedes lanzarlo como flotante y luego int. Esto también funcionará para cadenas flotantes o cadenas enteras.

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

Nota : Esto eliminará cualquier número después del decimal.

>>> int(float('38.2'))
38
Brandonbanks
fuente
1

Otro ejemplo de código para convertir un real / flotante en un entero usando variables. "vel" es un número real / flotante y se convierte al siguiente INTEGER más alto, "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
fuente
0

Dado que está solicitando la forma "más segura", le proporcionaré otra respuesta que no sea la respuesta principal.

Una manera fácil de asegurarse de no perder precisión es verificar si los valores serían iguales después de convertirlos.

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

Si el flotante es 1.0 por ejemplo, 1.0 es igual a 1. Por lo tanto, la conversión a int se ejecutará. Y si el valor flotante es 1.1, int (1.1) equivale a 1, y 1.1! = 1. Por lo tanto, el valor seguirá siendo un valor flotante y no perderá ninguna precisión.

Troya H
fuente
0

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

Niruttara
fuente
Una explicación a su respuesta es muy útil.
bibliophilsagar