Самый безопасный способ конвертировать float в целое число в Python?

216

Математический модуль Python содержит удобные функции, такие как floor& ceil. Эти функции принимают число с плавающей запятой и возвращают ближайшее целое число ниже или выше его. Однако эти функции возвращают ответ в виде числа с плавающей запятой. Например:

import math
f=math.floor(2.3)

Теперь fвозвращается:

2.0

Какой самый безопасный способ получить целое число из этого числа с плавающей точкой, без риска ошибки округления (например, если число с плавающей точкой эквивалентно 1.99999) или, возможно, мне следует использовать другую функцию в целом?

Боаз
источник
7
math.floor возвращает число с плавающей запятой в v2.6 , но оно возвращает целое число в v3 . На данный момент (почти через шесть лет после ОП) эта проблема может появляться редко.
sancho.s ReinstateMonicaCellio
Однако NumPy по-прежнему возвращает float, поэтому вопрос является действительным.
Vincenzooo

Ответы:

178

Все целые числа, которые могут быть представлены числами с плавающей запятой, имеют точное представление. Так что вы можете смело использовать intрезультат. Неточные представления возникают, только если вы пытаетесь представить рациональное число со знаменателем, который не является степенью двойки.

То, что это работает, совсем не тривиально! Это свойство представления IEEE с плавающей точкой, что int∘floor = ⌊⋅⌋, если величина рассматриваемых чисел достаточно мала, но возможны разные представления, где int (floor (2.3)) может быть 1.

Цитировать из Википедии ,

Любое целое число с абсолютным значением, меньшим или равным 2 24, может быть точно представлено в формате одинарной точности, а любое целое число с абсолютным значением, меньшим или равным 2 53, может быть точно представлено в формате двойной точности.

Philipp
источник
8
+1 для того, чтобы идти немного глубже. Вы также можете добавить краткое объяснение того, почему: en.wikipedia.org/wiki/Floating_point : D
Гордон Густафсон
В Python 2 int является таким же, как C int. Похоже, что в Python 3 нет ограничений на размер «int, stackoverflow.com/questions/13795758/…» . Значение «int» также зависит от операционной системы и аппаратного обеспечения. См. En.wikipedia. org / wiki / 64-bit_computing # 64-bit_data_models . Если вы программируете с использованием C-API, python 3, вам нужно быть очень осторожным с определением long и size_t на вашей платформе. docs.python.org/3 /c-api/long.html
Хуан,
114

Используйте int(your non integer number)это прибьет это.

print int(2.3) # "2"
print int(math.sqrt(5)) # "2"
srodriguex
источник
4
Это не сработает для отрицательных чисел: floorint
округление
1
@jochen Я тестировал int(-2.3)в дистрибутиве Python Canopy 2.7.6 и получил, -2как и ожидалось. Целые числа могут быть отрицательными, так же, как в формальном определении по математике.
srodriguex
5
Я согласен, int(-2.3)дает, -2как вы говорите, потому что это округляет 0, то есть вверх в этом случае. Напротив, используется оригинальный вопрос math.floor, который всегда округляется: math.floor(-2.3)дает -3.0.
Йохен
1
Это на самом деле не проблема. OP просто хочет получить целое число из результата math.floor, и этот ответ показывает, как преобразовать число с плавающей точкой в ​​целое число. Возьмите поплавок math.floorи intint(math.floor(2.3))
пропустите
4
Ты вообще читал вопрос? Он знает о функции int () , но спросил, можете ли вы столкнуться с проблемами с 1.9999 вместо 2.0. Ваш ответ даже не близок к ответу, вы пропустили весь смысл ...
Mayou36
48

Вы можете использовать функцию округления. Если вы не используете второй параметр (количество значащих цифр), я думаю, вы получите желаемое поведение.

IDLE выход.

>>> round(2.99999999999)
3
>>> round(2.6)
3
>>> round(2.5)
3
>>> round(2.4)
2
Wade73
источник
28
roundтакже возвращает число с плавающей запятой, по крайней мере, в Python 2.6.
Филипп
8
В Python 3.1.2 round возвращает int.
Роберт
2
Действительно, оба roundи floorвозвращают целые числа в Python 3.x. Поэтому я предполагаю, что вопрос касается Python 2.x.
Филипп
4
так может быть int(round(2.65))?
teewuane
1
почему round(6.5)дает 6? Кажется, что это своего рода ceil()плавающее число, когда во всех остальных случаях после десятичной дроби сразу после 5 (или больше). Почему это не работает в этом случае? или любой другой случай, когда число заканчивается на шесть, а после десятичной запятой есть 5 ...
Candh
43

Объединяя два предыдущих результата, мы имеем:

int(round(some_float))

Это превращает число с плавающей точкой в ​​целое число довольно надежно.

user3763109
источник
Что произойдет, если вы попытаетесь обогнуть очень длинный поплавок? По крайней мере, это вызовет исключение?
Агостино
@Agostino Что вы имеете в виду "очень долго плавать"?
kralyk
@kralyk Я имею в виду floatпредставление числа больше, чем intможет удержать нормаль . В Python 2 есть floatзначения, которые вы можете представить только с помощью long(после округления)?
Агостино
@кралык, ты имеешь ввиду после раунда? Итак, приведение их к int вызовет исключение или просто усечет их?
Агостино,
@Agostino Нет, int()функция производит или, intили на longоснове того, что нужно ...
kralyk
18

То, что это работает, совсем не тривиально! Это свойство представления IEEE с плавающей точкой, что int∘floor = ⌊⋅⌋, если величина рассматриваемых чисел достаточно мала, но возможны разные представления, где int (floor (2.3)) может быть 1.

Этот пост объясняет, почему он работает в этом диапазоне .

В двойном выражении вы можете без проблем представлять 32-битные целые числа. Там не может быть никаких вопросов округления. Точнее, double может представлять все целые числа от 2 53 до -2 53 включительно. .

Краткое объяснение : двойной может хранить до 53 двоичных цифр. Когда вам требуется больше, число дополняется нулями справа.

Отсюда следует, что 53 единицы - это наибольшее число, которое можно хранить без заполнения. Естественно, все (целые) числа, требующие меньше цифр, могут быть сохранены точно.

Добавление одного к 111 (опущено) 111 (53) приводит к 100 ... 000, (53 нуля). Как мы знаем, мы можем хранить 53 цифры, что делает заполнение нуля самым правым.

Отсюда 2 53 .


Более подробно: нам нужно рассмотреть, как работает IEEE-754 с плавающей запятой.

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

Затем число рассчитывается следующим образом (за исключением особых случаев, которые здесь не имеют значения):

-1 знак × 1.Мантисса × 2 Показатель степени - смещение

где смещение = 2 экспонента - 1 - 1 , то есть 1023 и 127 для двойной / одинарной точности соответственно.

Зная, что умножение на 2 X просто сдвигает все биты X влево, легко видеть, что любое целое число должно иметь все биты в мантиссе, которые заканчиваются справа от десятичной точки до нуля.

Любое целое число, кроме нуля, имеет следующую форму в двоичном виде:

1x ... x, где x -es представляют биты справа от MSB (самый старший бит).

Поскольку мы исключили ноль, всегда будет единица MSB, поэтому она не сохраняется. Чтобы сохранить целое число, мы должны привести его в вышеупомянутую форму: -1 знак × 1.Mantissa × 2 экспонента - смещение .

Это то же самое, что сдвигать биты над десятичной точкой, пока слева от MSB не будет только MSB. Все биты справа от десятичной точки затем сохраняются в мантиссе.

Отсюда видно, что мы можем хранить не более 52 двоичных цифр, кроме MSB.

Отсюда следует, что наибольшее число, в котором все биты хранятся явно

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

Для этого нам нужно установить показатель степени так, чтобы десятичная точка была смещена на 52 позиции. Если бы мы увеличили показатель степени на единицу, мы не смогли бы узнать цифру справа налево после десятичной точки.

111(omitted)111x.

По соглашению, это 0. Установив всю мантиссу на ноль, мы получим следующее число:

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

Это 1 с последующими 53 нулями, 52 сохраненными и 1 добавленными из-за показателя степени.

Это представляет 2 53 , который отмечает границу (и отрицательную и положительную), между которой мы можем точно представить все целые числа. Если бы мы хотели добавить один к 2 53 , мы должны были бы установить неявный ноль (обозначенный как x) на один, но это невозможно.

phant0m
источник
8

math.floorвсегда будет возвращать целое число и, следовательно int(math.floor(some_float)), никогда не приведет к ошибкам округления.

Ошибка округления может быть уже внесена math.floor(some_large_float), или даже при первом сохранении большого числа в плавающей запятой. (Большие числа могут потерять точность при хранении в числах с плавающей запятой.)


источник
7
Из: docs.python.org/2/library/math.html - math.floor (x) - Возвращает значение x в виде числа с плавающей запятой, наибольшее целочисленное значение меньше или равно x.
Билл Росмус
Зачем вам нужно вызывать math.floor, когда int уже делает то же самое?
Алекс
1
@Alex: intи floorвозвращать разные значения для отрицательных чисел, конечно.
8

Если вам нужно преобразовать строку с плавающей точкой в ​​int, вы можете использовать этот метод.

Пример: '38.0'к38

Чтобы преобразовать это в int, вы можете разыграть его как float, а затем int. Это также будет работать для строк с плавающей точкой или целочисленных строк.

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

Примечание . Это приведет к удалению любых чисел после запятой.

>>> int(float('38.2'))
38
brandonbanks
источник
1

Другой пример кода для преобразования real / float в целое число с использованием переменных. «vel» - это действительное число / число с плавающей запятой, которое преобразуется в следующий по величине 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
источник
0

Поскольку вы запрашиваете «самый безопасный» способ, я предоставлю другой ответ, отличный от верхнего.

Простой способ убедиться, что вы не теряете никакой точности, это проверить, будут ли значения равны после их преобразования.

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

Например, если число с плавающей запятой равно 1,0, 1,0 равно 1. Таким образом, преобразование в int будет выполнено. И если значение с плавающей точкой равно 1.1, int (1.1) равно 1, а значение 1.1! = 1. Таким образом, значение останется с плавающей точкой, и вы не потеряете никакой точности.

Трой Х
источник
0

DF [ 'column_name'] = ДФ [ 'column_name']. astype (INT)

Niruttara
источник
Объяснение вашего ответа имеет большое значение.
bibliophilsagar