Najbezpieczniejszy sposób na konwersję liczby zmiennoprzecinkowej na liczbę całkowitą w pythonie?

215

Moduł matematyczny Pythona zawiera przydatne funkcje, takie jak floor& ceil. Funkcje te przyjmują liczbę zmiennoprzecinkową i zwracają najbliższą liczbę całkowitą poniżej lub powyżej niej. Jednak te funkcje zwracają odpowiedź jako liczbę zmiennoprzecinkową. Na przykład:

import math
f=math.floor(2.3)

Teraz fzwraca:

2.0

Jaki jest najbezpieczniejszy sposób na uzyskanie liczby całkowitej z tego elementu zmiennoprzecinkowego bez ryzyka wystąpienia błędów zaokrąglania (na przykład jeśli liczba zmiennoprzecinkowa jest równa 1,99999), a może powinienem użyć innej funkcji?

Boaz
źródło
7
math.floor zwraca liczbę zmiennoprzecinkową w v2.6 , ale zwraca liczbę całkowitą w v3 . W tym momencie (prawie sześć lat po PO) ten problem może pojawiać się rzadko.
sancho.s ReinstateMonicaCellio 21.04.16
jednak numpy nadal zwraca liczbę zmiennoprzecinkową, więc pytanie jest prawidłowe.
Vincenzooo

Odpowiedzi:

178

Wszystkie liczby całkowite, które mogą być reprezentowane przez liczby zmiennoprzecinkowe, mają dokładną reprezentację. Możesz więc bezpiecznie wykorzystać intwynik. Nieprecyzyjne reprezentacje występują tylko wtedy, gdy próbujesz reprezentować liczbę wymierną o mianowniku, który nie jest potęgą dwóch.

To wcale nie jest trywialne! Właściwością reprezentacji zmiennoprzecinkowej IEEE jest to, że int∘floor = ⌊⋅⌋, jeśli wielkość danych liczb jest wystarczająco mała, ale możliwe są różne reprezentacje, gdzie int (floor (2.3)) może wynosić 1.

Cytat z Wikipedii :

Każda liczba całkowita o wartości bezwzględnej mniejszej lub równej 2 24 może być dokładnie przedstawiona w formacie pojedynczej precyzji, a każda liczba całkowita o wartości bezwzględnej mniejszej lub równej 2 53 może być dokładnie przedstawiona w formacie podwójnej precyzji.

Philipp
źródło
8
+1 za zejście nieco głębiej. Możesz także rzucić krótkie wyjaśnienie, dlaczego: en.wikipedia.org/wiki/Floating_point : D
Gordon Gustafson
W Pythonie 2 „int” to to samo, co „int”. W Pythonie 3 wydaje się, że nie ma ograniczeń co do wielkości „int, stackoverflow.com/questions/13795758/.... Znaczenie„ int ”zależy również od systemu operacyjnego i sprzętu. Patrz en.wikipedia. org / wiki / 64-bit_computing # 64-bit_data_models . Jeśli programujesz z C-API, python 3 musisz bardzo uważać na to, jaka jest definicja long i size_t na swojej platformie. docs.python.org/3 /c-api/long.html
Juan
112

Użycie int(your non integer number)go przybije.

print int(2.3) # "2"
print int(math.sqrt(5)) # "2"
srodriguex
źródło
4
To nie zadziała dla liczb ujemnych: floorzaokrągla w dół, a intzaokrągla w kierunku 0.
jochen
1
@jochen Przetestowałem int(-2.3)w dystrybucji Python Canopy 2.7.6 i dostałem -2zgodnie z oczekiwaniami. Liczby całkowite mogą być ujemne, podobnie jak w formalnej definicji matematyki.
srodriguex
5
Zgadzam się, int(-2.3)daje, -2jak mówisz, ponieważ zaokrągla w stronę 0, tj. W tym przypadku w górę. Natomiast użyte pierwotne pytanie math.floor, które zawsze zaokrągla w dół: math.floor(-2.3)daje -3.0.
jochen
1
To nie jest tak naprawdę problem. OP chce tylko, aby wynik był liczbą całkowitą math.floor, a ta odpowiedź pokazuje, jak przekonwertować liczbę zmiennoprzecinkową na liczbę całkowitą. Wyjmij pływak math.floori intint(math.floor(2.3))
przepuść
4
Czy w ogóle przeczytałeś pytanie? Jest świadomy funkcji int () , ale zapytał, czy możesz napotkać problemy z 1.9999 zamiast 2.0. Twoja odpowiedź wcale nie jest bliska odpowiedzi, przegapiłeś cały punkt ...
Mayou36
48

Możesz użyć funkcji okrągłej. Jeśli nie użyjesz drugiego parametru (liczby cyfr znaczących), myślę, że uzyskasz pożądane zachowanie.

Wyjście IDLE.

>>> round(2.99999999999)
3
>>> round(2.6)
3
>>> round(2.5)
3
>>> round(2.4)
2
Wade73
źródło
28
roundzwraca również liczbę zmiennoprzecinkową, przynajmniej w Pythonie 2.6.
Philipp
8
W Pythonie 3.1.2 runda zwraca liczbę całkowitą.
Robert
2
Rzeczywiście, obie roundi floorzwracają liczby całkowite w Pythonie 3.x. Przypuszczam więc, że pytanie dotyczy Pythona 2.x.
Philipp
4
więc może int(round(2.65))?
teewuane
1
dlaczego round(6.5)daje 6? Wydaje się, że jest ceil()to liczba zmiennoprzecinkowa, gdy we wszystkich innych przypadkach występuje natychmiastowa liczba 5 (lub większa do 9) po przecinku. Dlaczego to nie działa w tym przypadku? lub w każdym innym przypadku, gdy liczba kończy się cyfrą szóstą, a po przecinku jest
cyfra
43

Łącząc dwa poprzednie wyniki, mamy:

int(round(some_float))

Konwertuje to liczbę zmiennoprzecinkową na liczbę całkowitą dość niezawodnie.

użytkownik3763109
źródło
Co się stanie, jeśli spróbujesz zaokrąglić bardzo długi pływak? Czy to przynajmniej wzbudzi wyjątek?
Agostino
@Agostino Co masz na myśli mówiąc „bardzo długi pływak”?
kralyk
@kralyk Mam na myśli floatliczbę reprezentującą większą niż normalna int. Czy w Pythonie 2 są floatwartości, które można reprezentować tylko za pomocą long(po zaokrągleniu)?
Agostino,
@kralyk masz na myśli po rundzie? Czy więc rzucenie ich na int spowoduje wyjątek, czy po prostu je obciąć?
Agostino,
@Agostino Nie, int()funkcja generuje albo intalbo w longoparciu o to, co jest potrzebne ...
kralyk
18

To wcale nie jest trywialne! Właściwością reprezentacji zmiennoprzecinkowej IEEE jest to, że int∘floor = ⌊⋅⌋, jeśli wielkość danych liczb jest wystarczająco mała, ale możliwe są różne reprezentacje, gdzie int (floor (2.3)) może wynosić 1.

Ten post wyjaśnia, dlaczego działa w tym zakresie .

Podwójnie możesz bez problemu reprezentować 32-bitowe liczby całkowite. Nie może być żadnych problemów z zaokrąglaniem. Dokładniej, liczby podwójne mogą reprezentować wszystkie liczby całkowite między 2 53 a -2 53 włącznie .

Krótkie wyjaśnienie : W podwójnej pamięci można zapisać do 53 cyfr binarnych. Gdy potrzebujesz więcej, liczba jest wypełniona zerami po prawej stronie.

Wynika z tego, że 53 z nich to największa liczba, którą można przechowywać bez wypełnienia. Oczywiście wszystkie liczby (całkowite) wymagające mniejszej liczby cyfr mogą być przechowywane dokładnie.

Dodanie jednego do 111 (pominięte) 111 (53 jedynki) daje 100 ... 000, (53 zera). Jak wiemy, możemy przechowywać 53 cyfry, co sprawia, że ​​dopełnienie zera po prawej stronie jest zerowe.

Stąd pochodzi 2 53 .


Więcej szczegółów: Musimy rozważyć, jak działa zmiennoprzecinkowy IEEE-754.

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

Liczbę oblicza się następnie w następujący sposób (z wyłączeniem szczególnych przypadków, które nie są tu istotne):

-1 znak × 1. mantysa × 2 wykładnik - błąd

gdzie odchylenie = 2 wykładnik - 1 - 1 , tj. 1023 i 127 odpowiednio dla podwójnej / pojedynczej precyzji.

Wiedząc, że pomnożenie przez 2 X po prostu przesuwa wszystkie bity X miejsc w lewo, łatwo zauważyć, że każda liczba całkowita musi mieć wszystkie bity w mantysie, które kończą się po prawej stronie przecinka dziesiętnego na zero.

Każda liczba całkowita oprócz zera ma następującą postać binarną:

1x ... x, gdzie x-y reprezentują bity po prawej stronie MSB (najbardziej znaczący bit).

Ponieważ wykluczyliśmy zero, zawsze będzie MSB, który jest jeden - dlatego nie jest przechowywany. Aby zapisać liczbę całkowitą, musimy doprowadzić ją do powyższej postaci: znak -1 × 1. mantysa × 2 wykładnik - odchylenie .

To oznacza to samo, co przesunięcie bitów ponad przecinek dziesiętny, dopóki MSB nie będzie tylko po lewej stronie MSB. Wszystkie bity po prawej stronie przecinka są następnie zapisywane w mantysie.

Z tego wynika, że ​​oprócz MSB możemy przechowywać maksymalnie 52 cyfry binarne.

Wynika z tego, że najwyższą liczbą, w której wszystkie bity są jawnie przechowywane, jest

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

W tym celu musimy ustawić wykładnik, tak aby przecinek dziesiętny został przesunięty o 52 miejsca. Gdybyśmy zwiększyli wykładnik o jeden, nie możemy znać cyfry od prawej do lewej po przecinku.

111(omitted)111x.

Zgodnie z konwencją wynosi 0. Po ustawieniu całej mantysy na zero otrzymujemy następującą liczbę:

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

To 1, po którym następuje 53 zera, 52 zapisane i 1 dodane ze względu na wykładnik potęgi.

Reprezentuje 2 53 , co oznacza granicę (zarówno ujemną, jak i dodatnią), pomiędzy którą możemy dokładnie przedstawić wszystkie liczby całkowite. Gdybyśmy chcieli dodać jeden do 2 53 , musielibyśmy ustawić wartość domyślną zero (oznaczoną przez x) na jeden, ale to niemożliwe.

phant0m
źródło
8

math.floorzawsze zwraca liczbę całkowitą, a zatem int(math.floor(some_float))nigdy nie wprowadza błędów zaokrąglania.

Błąd zaokrąglania może już zostać wprowadzony math.floor(some_large_float), a nawet w przypadku przechowywania dużej liczby w liczbach zmiennoprzecinkowych. (Duże liczby mogą stracić precyzję, gdy są przechowywane w liczbach zmiennoprzecinkowych).


źródło
7
From: docs.python.org/2/library/math.html - math.floor (x) - Zwraca podłogę x jako liczbę zmiennoprzecinkową, największą wartość całkowitą mniejszą lub równą x.
Bill Rosmus
Dlaczego musisz wywoływać funkcję math.floor, skoro int już robi to samo?
Alex
1
@Alex: inti oczywiście floorzwracają różne wartości liczb ujemnych.
8

Jeśli chcesz przekonwertować zmiennoprzecinkowe zmiennoprzecinkowe na int, możesz użyć tej metody.

Przykład: '38.0'do38

Aby przekonwertować to na liczbę całkowitą, możesz rzucić ją jako liczbę zmiennoprzecinkową, a następnie liczbę całkowitą. Będzie to również działać w przypadku ciągów pływających lub ciągów całkowitoliczbowych.

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

Uwaga : Spowoduje to usunięcie wszystkich liczb po przecinku.

>>> int(float('38.2'))
38
brandonbanks
źródło
1

Kolejny przykład kodu do konwersji wartości rzeczywistej / zmiennoprzecinkowej na liczbę całkowitą przy użyciu zmiennych. „vel” to liczba rzeczywista / zmiennoprzecinkowa i konwertowana na następną najwyższą liczbę całkowitą, „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
źródło
0

Ponieważ pytasz o „najbezpieczniejszy” sposób, podam inną odpowiedź niż najlepsza.

Prostym sposobem, aby upewnić się, że nie stracisz żadnej precyzji, jest sprawdzenie, czy wartości byłyby równe po ich konwersji.

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

Jeśli liczba zmiennoprzecinkowa wynosi na przykład 1.0, 1.0 jest równe 1. Tak więc konwersja na int zostanie wykonana. A jeśli liczba zmiennoprzecinkowa wynosi 1.1, int (1.1) równa się 1, a wartość 1.1! = 1. Tak więc wartość pozostanie zmiennoprzecinkowa i nie stracisz żadnej precyzji.

Troy H.
źródło
0

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

Niruttara
źródło
Wyjaśnienie twojej odpowiedzi jest bardzo ważne.
bibliophilsagar