Der sicherste Weg, Float in Python in Integer umzuwandeln?

215

Pythons Mathematikmodul enthält praktische Funktionen wie floor& ceil. Diese Funktionen nehmen eine Gleitkommazahl und geben die nächste Ganzzahl darunter oder darüber zurück. Diese Funktionen geben die Antwort jedoch als Gleitkommazahl zurück. Beispielsweise:

import math
f=math.floor(2.3)

Jetzt fkehrt zurück:

2.0

Was ist der sicherste Weg, um eine Ganzzahl aus diesem Float herauszuholen, ohne das Risiko von Rundungsfehlern einzugehen (z. B. wenn der Float 1,99999 entspricht), oder sollte ich eine andere Funktion verwenden?

Boas
quelle
7
math.floor Gibt in Version 2.6 einen Float zurück, in Version 3 jedoch eine Ganzzahl . Zu diesem Zeitpunkt (fast sechs Jahre nach dem OP) könnte dieses Problem selten auftreten.
sancho.s ReinstateMonicaCellio
Da numpy jedoch immer noch float zurückgibt, ist die Frage gültig.
Vincenzooo

Antworten:

178

Alle Ganzzahlen, die durch Gleitkommazahlen dargestellt werden können, haben eine genaue Darstellung. So können Sie intdas Ergebnis sicher verwenden . Ungenaue Darstellungen treten nur auf, wenn Sie versuchen, eine rationale Zahl mit einem Nenner darzustellen, der keine Zweierpotenz ist.

Dass dies funktioniert, ist überhaupt nicht trivial! Es ist eine Eigenschaft der IEEE-Gleitkommadarstellung, dass int∘floor = ⌊⋅⌋ ist, wenn die Größe der fraglichen Zahlen klein genug ist, aber unterschiedliche Darstellungen möglich sind, wenn int (floor (2.3)) 1 sein könnte.

Um aus Wikipedia zu zitieren ,

Jede Ganzzahl mit einem Absolutwert kleiner oder gleich 2 24 kann im Format mit einfacher Genauigkeit genau dargestellt werden, und jede Ganzzahl mit einem Absolutwert kleiner oder gleich 2 53 kann im Format mit doppelter Genauigkeit genau dargestellt werden.

Philipp
quelle
8
+1 für etwas tiefer gehen. Sie können auch eine kurze Erklärung abgeben , warum: en.wikipedia.org/wiki/Floating_point : D
Gordon Gustafson
In Python 2 ist ein "int" dasselbe wie ein C "int". In Python 3 scheint die Größe eines "int", stackoverflow.com/questions/13795758/… , unbegrenzt zu sein . Die Bedeutung von "int" hängt auch vom Betriebssystem und der zugrunde liegenden Hardware ab. Siehe en.wikipedia. org / wiki / 64-bit_computing # 64-bit_data_models . Wenn Sie mit der C-API Python 3 programmieren, müssen Sie bei der Definition von long und size_t auf Ihrer Plattform sehr vorsichtig sein. docs.python.org/3 /c-api/long.html
Juan
112

Verwendung int(your non integer number)wird es nageln.

print int(2.3) # "2"
print int(math.sqrt(5)) # "2"
srodriguex
quelle
4
Dies funktioniert nicht für negative Zahlen: floorRunden ab, während intRunden in Richtung 0.
Jochen
1
@jochen Ich habe int(-2.3)in Python Distribution Canopy 2.7.6 getestet und -2wie erwartet bekommen. Ganzzahlen können genauso negativ sein wie in der formalen mathematischen Definition.
Srodriguex
5
Ich stimme zu, int(-2.3)gibt, -2wie Sie sagen, weil es in Richtung 0, dh in diesem Fall rundet . Im Gegensatz dazu wird die ursprüngliche Frage verwendet math.floor, die immer abrundet: math.floor(-2.3)gibt -3.0.
Jochen
1
Das ist eigentlich kein Problem. OP möchte nur eine Ganzzahl aus dem Ergebnis von math.floor, und diese Antwort zeigt, wie ein Float in eine Ganzzahl umgewandelt wird. Nehmen Sie den Schwimmer aus math.floorund intint(math.floor(2.3))
leiten
4
Hast du die Frage überhaupt gelesen? Er ist sich der Funktion int () bewusst , hat jedoch gefragt, ob Sie möglicherweise Probleme mit 1.9999 anstelle von 2.0 haben. Ihre Antwort ist nicht einmal annähernd eine Antwort, Sie haben den ganzen Punkt verpasst ...
Mayou36
48

Sie können die Rundungsfunktion verwenden. Wenn Sie keinen zweiten Parameter (Anzahl der signifikanten Stellen) verwenden, erhalten Sie wahrscheinlich das gewünschte Verhalten.

IDLE-Ausgabe.

>>> round(2.99999999999)
3
>>> round(2.6)
3
>>> round(2.5)
3
>>> round(2.4)
2
Wade73
quelle
28
roundGibt auch eine Float-Nummer zurück, zumindest in Python 2.6.
Philipp
8
In Python 3.1.2 gibt round ein int zurück.
Robert
2
In der Tat sind beide roundund floorreturn Ganzzahlen in Python 3.x. Ich nehme also an, dass die Frage Python 2.x betrifft.
Philipp
4
also vielleicht int(round(2.65))?
Teewuane
1
warum round(6.5)gibt es 6? ceil()In allen anderen Fällen scheint es eine Art Float zu geben, wenn unmittelbar nach der Dezimalstelle eine 5 (oder höher bis 9) steht. Warum funktioniert das in diesem Fall nicht? oder in jedem anderen Fall, wenn die Zahl mit einer Sechs endet und eine 5 direkt nach der Dezimalstelle steht ...
Candh
43

Wenn wir zwei der vorherigen Ergebnisse kombinieren, haben wir:

int(round(some_float))

Dies wandelt einen Float ziemlich zuverlässig in eine Ganzzahl um.

user3763109
quelle
Was passiert, wenn Sie versuchen, einen sehr langen Schwimmer abzurunden? Wird dies zumindest eine Ausnahme auslösen?
Agostino
@Agostino Was meinst du mit "sehr langer Schwimmer"?
Kralyk
@kralyk Ich meine eine floatZahl, die größer ist als das, was ein Normaler inthalten kann. Gibt es in Python 2 floatWerte, die Sie nur mit a long(nach dem Runden) darstellen können?
Agostino
@kralyk meinst du, nach der Runde? Würde das Casting von int eine Ausnahme auslösen oder sie einfach abschneiden?
Agostino
@ Agostino Nein, die int()Funktion erzeugt entweder ein intoder ein longbasierend auf dem, was benötigt wird ...
kralyk
18

Dass dies funktioniert, ist überhaupt nicht trivial! Es ist eine Eigenschaft der IEEE-Gleitkommadarstellung, dass int∘floor = ⌊⋅⌋ ist, wenn die Größe der fraglichen Zahlen klein genug ist, aber unterschiedliche Darstellungen möglich sind, wenn int (floor (2.3)) 1 sein könnte.

Dieser Beitrag erklärt, warum es in diesem Bereich funktioniert .

In einem Double können Sie problemlos 32-Bit-Ganzzahlen darstellen. Es kann keine Rundungsprobleme geben. Genauer gesagt können Doubles alle ganzen Zahlen zwischen und einschließlich 2 53 und -2 53 darstellen .

Kurze Erklärung : Ein Double kann bis zu 53 Binärziffern speichern. Wenn Sie mehr benötigen, wird die Zahl rechts mit Nullen aufgefüllt.

Daraus folgt, dass 53 Einsen die größte Zahl sind, die ohne Auffüllen gespeichert werden kann. Natürlich können alle (ganzzahligen) Zahlen, die weniger Ziffern erfordern, genau gespeichert werden.

Addiert man eins zu 111 (weggelassen) 111 (53 Einsen) ergibt 100 ... 000 (53 Nullen). Wie wir wissen, können wir 53 Ziffern speichern, was die Nullpunktauffüllung ganz rechts ergibt.

Hier kommt 2 53 her .


Weitere Details: Wir müssen überlegen, wie IEEE-754-Gleitkomma funktioniert.

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

Die Anzahl wird dann wie folgt berechnet (ausgenommen hier irrelevante Sonderfälle):

-1 Vorzeichen × 1. Mantisse × 2 Exponent - Vorspannung

wobei Bias = 2 Exponent - 1 - 1 ist , dh 1023 und 127 für doppelte / einfache Genauigkeit.

Wenn man weiß, dass das Multiplizieren mit 2 X einfach alle Bits X nach links verschiebt, ist es leicht zu erkennen, dass jede Ganzzahl alle Bits in der Mantisse haben muss, die rechts vom Dezimalpunkt auf Null enden.

Jede Ganzzahl außer Null hat die folgende Form in Binärform:

1x ... x wobei die x -es die Bits rechts vom MSB darstellen (höchstwertiges Bit).

Da wir Null ausgeschlossen haben, gibt es immer ein MSB, das Eins ist - weshalb es nicht gespeichert wird. Um die ganze Zahl zu speichern, müssen wir sie in die oben genannte Form bringen: -1 Vorzeichen × 1. Mantisse × 2 Exponent - Bias .

Das heißt, Sie verschieben die Bits über den Dezimalpunkt, bis nur noch das MSB links vom MSB angezeigt wird. Alle Bits rechts vom Dezimalpunkt werden dann in der Mantisse gespeichert.

Daraus können wir ersehen, dass wir neben dem MSB höchstens 52 Binärziffern speichern können.

Daraus folgt, dass die höchste Zahl, in der alle Bits explizit gespeichert sind, ist

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

Dazu müssen wir den Exponenten so einstellen, dass der Dezimalpunkt um 52 Stellen verschoben wird. Wenn wir den Exponenten um eins erhöhen, können wir die Ziffer rechts nach links nach dem Dezimalpunkt nicht kennen.

111(omitted)111x.

Konventionell ist es 0. Wenn Sie die gesamte Mantisse auf Null setzen, erhalten Sie die folgende Nummer:

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

Das ist eine 1, gefolgt von 53 Nullen, 52 gespeicherten und 1 aufgrund des Exponenten hinzugefügten.

Es stellt 2 53 dar , was die Grenze (sowohl negativ als auch positiv) markiert, zwischen der wir alle ganzen Zahlen genau darstellen können. Wenn wir eins zu 2 53 addieren wollten , müssten wir die implizite Null (bezeichnet mit x) auf eins setzen, aber das ist unmöglich.

phant0m
quelle
8

math.floorgibt immer eine Ganzzahl zurück und führt daher int(math.floor(some_float))niemals zu Rundungsfehlern.

Der Rundungsfehler kann jedoch bereits in math.floor(some_large_float)oder sogar beim Speichern einer großen Anzahl in einem Float eingeführt werden. (Große Zahlen können an Genauigkeit verlieren, wenn sie in Schwimmern gespeichert werden.)


quelle
7
Von: docs.python.org/2/library/math.html - math.floor (x) - Gibt den Boden von x als Gleitkommawert zurück, den größten ganzzahligen Wert kleiner oder gleich x.
Bill Rosmus
Warum müssen Sie math.floor aufrufen, wenn int bereits dasselbe tut?
Alex
1
@Alex: intund floornatürlich unterschiedliche Werte für negative Zahlen zurückgeben.
8

Wenn Sie einen String float in einen int konvertieren müssen, können Sie diese Methode verwenden.

Beispiel: '38.0'bis38

Um dies in ein Int umzuwandeln, können Sie es als Float und dann als Int umwandeln. Dies funktioniert auch für Float-Strings oder Integer-Strings.

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

Hinweis : Dadurch werden alle Zahlen nach der Dezimalstelle entfernt.

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

Ein weiteres Codebeispiel zum Konvertieren eines Real / Float in eine Ganzzahl mithilfe von Variablen. "vel" ist eine reelle / float-Zahl und wird in den nächsthöheren INTEGER "newvel" umgewandelt.

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
quelle
0

Da Sie nach dem "sichersten" Weg fragen, werde ich eine andere Antwort als die Top-Antwort geben.

Eine einfache Möglichkeit, um sicherzustellen, dass Sie keine Genauigkeit verlieren, besteht darin, zu überprüfen, ob die Werte nach der Konvertierung gleich sind.

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

Wenn der float beispielsweise 1.0 ist, ist 1.0 gleich 1. Die Konvertierung in int wird also ausgeführt. Und wenn der Float 1.1 ist, entspricht int (1.1) 1 und 1.1! = 1. Der Wert bleibt also ein Float und Sie verlieren keine Genauigkeit.

Troy H.
quelle
0

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

Niruttara
quelle
Eine Erklärung zu Ihrer Antwort reicht weit.
Bibliophilsagar