Il modo più sicuro per convertire float in intero in Python?


216

Il modulo matematico di Python contiene utili funzioni come floor& ceil. Queste funzioni prendono un numero in virgola mobile e restituiscono il numero intero più vicino sotto o sopra di esso. Tuttavia, queste funzioni restituiscono la risposta come un numero in virgola mobile. Per esempio:

import math
f=math.floor(2.3)

Ora fritorna:

2.0

Qual è il modo più sicuro per ottenere un numero intero da questo float, senza correre il rischio di errori di arrotondamento (ad esempio se il float è l'equivalente di 1.99999) o forse dovrei usare un'altra funzione del tutto?


7
math.floor restituisce un float in v2.6 , ma restituisce un numero intero in v3 . A questo punto (quasi sei anni dopo il PO), questo problema potrebbe presentarsi raramente.
sancho.s ReinstateMonicaCellio il

tuttavia numpy restituisce comunque float, quindi la domanda è valida.
Vincenzooo,

Risposte:


178

Tutti i numeri interi che possono essere rappresentati da numeri in virgola mobile hanno una rappresentazione esatta. Quindi puoi usare tranquillamente intil risultato. Le rappresentazioni inesatte si verificano solo se si sta tentando di rappresentare un numero razionale con un denominatore che non è una potenza di due.

Che funzioni non è affatto banale! È una proprietà della rappresentazione in virgola mobile IEEE che int∘floor = ⌊⋅⌋ se l'entità dei numeri in questione è abbastanza piccola, ma sono possibili diverse rappresentazioni dove int (floor (2.3)) potrebbe essere 1.

Per citare da Wikipedia ,

Qualsiasi numero intero con valore assoluto inferiore o uguale a 2 24 può essere rappresentato esattamente nel formato a precisione singola e qualsiasi numero intero con valore assoluto inferiore o uguale a 2 53 può essere rappresentato esattamente nel formato a precisione doppia.


8
+1 per andare un po 'più in profondità. Potresti anche dare una breve spiegazione del perché: en.wikipedia.org/wiki/Floating_point : D
Gordon Gustafson

In Python 2, un "int" è uguale a un "int" C. In Python 3, sembra che non vi siano limiti alle dimensioni di "int, stackoverflow.com/questions/13795758/… . Il significato di" int "dipende anche dal sistema operativo e dall'hardware sottostante. Vedi en.wikipedia. org / wiki / 64-bit_computing # 64-bit_data_models Se stai programmando con l'API C, python 3 devi stare molto attento a quale definizione di long e size_t è sulla tua piattaforma. docs.python.org/3 /c-api/long.html
Juan,

114

L'uso int(your non integer number)lo inchioderà.

print int(2.3) # "2"
print int(math.sqrt(5)) # "2"

4
Questo non funzionerà per i numeri negativi: floorarrotondamento per int
difetto

1
@jochen Ho testato int(-2.3)nella distribuzione Python Canopy 2.7.6 e ottenuto -2come previsto. I numeri interi possono essere negativi, allo stesso modo nella definizione matematica formale.
srodriguex,

5
Sono d'accordo, int(-2.3)-2come dici tu, perché si arrotonda verso 0, vale a dire in questo caso. Al contrario, la domanda originale utilizzata math.floor, che arrotonda sempre per difetto: math.floor(-2.3)-3.0.
Jochen,

1
Questo non è davvero un problema. OP vuole solo un numero intero dal risultato di math.floor, e questa risposta mostra come convertire un float in un numero intero. Prendi il galleggiante math.floore attraversalo int, problema risolto:int(math.floor(2.3))
JMTyler

4
Hai persino letto la domanda? È a conoscenza della funzione int () , ma ha chiesto se si potrebbero riscontrare problemi con 1.9999 anziché 2.0. La tua risposta non è nemmeno vicina a una risposta, ti sei perso l'intero punto ...
Mayou36

48

È possibile utilizzare la funzione rotonda. Se non usi un secondo parametro (numero di cifre significative), penso che otterrai il comportamento che desideri.

Uscita IDLE.

>>> round(2.99999999999)
3
>>> round(2.6)
3
>>> round(2.5)
3
>>> round(2.4)
2

28
roundrestituisce anche un numero float, almeno in Python 2.6.
Philipp

8
In Python 3.1.2, round restituisce un int.
robert

2
In effetti, entrambi rounde floorrestituiscono numeri interi in Python 3.x. Quindi suppongo che la domanda riguardi Python 2.x.
Philipp

4
quindi forse int(round(2.65))?
teewuane,

1
perché round(6.5)dà 6? Sembra un po ceil()'float quando c'è un immediato 5 (o maggiore fino a 9) dopo il decimale in tutti gli altri casi. Perché questo non funziona in questo caso? o qualsiasi altro caso in cui il numero termina con un sei e c'è un 5 subito dopo il decimale ...
candh,

43

Combinando due dei risultati precedenti, abbiamo:

int(round(some_float))

Questo converte un float in un numero intero in modo abbastanza affidabile.


Cosa succede se si tenta di arrotondare un galleggiante molto lungo? Ciò solleverà almeno un'eccezione?
Agostino,

@Agostino Cosa intendi con "galleggiante molto lungo"?
kralyk

@kralyk Voglio dire che floatrappresenta un numero maggiore di quello che intpuò contenere un normale . In Python 2, ci sono floatvalori che puoi rappresentare solo usando un long(dopo l'arrotondamento)?
Agostino,

@kralyk intendi, dopo il round? Quindi, lanciarli per int solleva un'eccezione o semplicemente li tronca?
Agostino,

@Agostino No, la int()funzione produce uno into uno longbasato su ciò che è necessario ...
kralyk

18

Che funzioni non è affatto banale! È una proprietà della rappresentazione in virgola mobile IEEE che int∘floor = ⌊⋅⌋ se l'entità dei numeri in questione è abbastanza piccola, ma sono possibili diverse rappresentazioni dove int (floor (2.3)) potrebbe essere 1.

Questo post spiega perché funziona in quell'intervallo .

In un doppio, puoi rappresentare numeri interi a 32 bit senza problemi. Non ci possono essere problemi di arrotondamento. Più precisamente, i doppi possono rappresentare tutti i numeri interi compresi tra 2 53 e -2 53 compresi .

Breve spiegazione : un doppio può memorizzare fino a 53 cifre binarie. Quando hai bisogno di più, il numero è riempito con zero sulla destra.

Ne consegue che 53 sono il numero più grande che può essere memorizzato senza imbottitura. Naturalmente, tutti i numeri (interi) che richiedono meno cifre possono essere memorizzati con precisione.

Aggiungendo uno a 111 (omesso) 111 (53 quelli) si ottengono 100 ... 000, (53 zero). Come sappiamo, possiamo memorizzare 53 cifre, il che rende il riempimento zero più a destra.

Ecco da dove proviene 2 53 .


Maggiori dettagli: dobbiamo considerare come funziona IEEE-754 in virgola mobile.

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

Il numero viene quindi calcolato come segue (esclusi casi speciali che qui non sono rilevanti):

-1 segno × 1.mantissa × 2 esponente - bias

dove bias = 2 esponente - 1 - 1 , ovvero 1023 e 127 rispettivamente per doppia / singola precisione.

Sapendo che la moltiplicazione per 2 X sposta semplicemente tutti i bit X posti a sinistra, è facile vedere che qualsiasi numero intero deve avere tutti i bit nella mantissa che finiscono a destra del punto decimale su zero.

Qualsiasi numero intero tranne zero ha la seguente forma in binario:

1x ... x dove le x -es rappresentano i bit a destra dell'MSB (bit più significativo).

Poiché abbiamo escluso zero, ci sarà sempre un MSB che è uno, motivo per cui non è memorizzato. Per memorizzare il numero intero, dobbiamo portarlo nella forma sopra menzionata: -1 segno × 1.mantissa × 2 esponente - bias .

Ciò equivale a spostare i bit sopra il punto decimale fino a quando non c'è solo l'MSB verso la sinistra dell'MSB. Tutti i bit a destra del punto decimale vengono quindi memorizzati nella mantissa.

Da questo, possiamo vedere che possiamo memorizzare al massimo 52 cifre binarie oltre al MSB.

Ne consegue che il numero più alto in cui tutti i bit sono memorizzati in modo esplicito è

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

Per questo, dobbiamo impostare l'esponente, in modo che il punto decimale venga spostato di 52 posizioni. Se dovessimo aumentare l'esponente di uno, non possiamo conoscere la cifra a destra dopo il punto decimale.

111(omitted)111x.

Per convenzione, è 0. Impostando l'intera mantissa su zero, riceviamo il seguente numero:

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

Questo è un 1 seguito da 53 zero, 52 memorizzati e 1 aggiunto a causa dell'esponente.

Rappresenta 2 53 , che segna il confine (sia negativo che positivo) tra cui possiamo rappresentare accuratamente tutti i numeri interi. Se volessimo aggiungere uno a 2 53 , dovremmo impostare lo zero implicito (indicato da x) su uno, ma è impossibile.


8

math.floorrestituirà sempre un numero intero e quindi int(math.floor(some_float))non introdurrà mai errori di arrotondamento.

L'errore di arrotondamento potrebbe essere già stato introdotto math.floor(some_large_float), comunque, o anche quando si memorizza un numero elevato in un float in primo luogo. (I numeri grandi possono perdere la precisione se conservati in galleggianti.)


7
Da: docs.python.org/2/library/math.html - math.floor (x) - Restituisce il piano di x come float, il valore intero più grande minore o uguale a x.
Bill Rosmus,

Perché devi chiamare math.floor quando int fa già la stessa cosa?
Alex,

1
@Alex: inte floorrestituisce valori diversi per i numeri negativi, ovviamente.

8

Se devi convertire un float di stringa in un int puoi usare questo metodo.

Esempio: '38.0'a38

Per convertirlo in un int puoi lanciarlo come float e poi un int. Questo funzionerà anche con stringhe float o stringhe intere.

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

Nota : questo rimuoverà tutti i numeri dopo il decimale.

>>> int(float('38.2'))
38

1

Un altro esempio di codice per convertire un vero / float in un numero intero usando le variabili. "vel" è un numero reale / float e convertito nel successivo INTEGER più 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))

0

Dal momento che stai chiedendo il modo più "sicuro", fornirò un'altra risposta diversa dalla risposta migliore.

Un modo semplice per assicurarsi di non perdere precisione è verificare se i valori sarebbero uguali dopo averli convertiti.

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

Se il float è 1.0, ad esempio, 1.0 è uguale a 1. Quindi verrà eseguita la conversione in int. E se il float è 1.1, int (1.1) equivale a 1 e 1.1! = 1. Quindi il valore rimarrà un float e non perderete alcuna precisione.


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.