Самый безопасный способ конвертировать число с плавающей точкой в ​​Python? - PullRequest
183 голосов
/ 02 августа 2010

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

import math
f=math.floor(2.3)

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

2.0

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

Ответы [ 9 ]

157 голосов
/ 02 августа 2010

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

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

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

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

96 голосов
/ 26 января 2013

Использование int(your non integer number) пригвоздит его.

print int(2.3) # "2"
print int(math.sqrt(5)) # "2"
44 голосов
/ 02 августа 2010

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

IDLE output.

>>> round(2.99999999999)
3
>>> round(2.6)
3
>>> round(2.5)
3
>>> round(2.4)
2
38 голосов
/ 21 июня 2014

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

int(round(some_float))

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

18 голосов
/ 26 января 2013

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

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

В двойном выражении вы можете без проблем представлять 32-битные целые числа.В не может быть проблем с округлением.Точнее, двойные числа могут представлять все целых чисел между 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.mantissa × 2 показатель степени - смещение

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

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

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

1x ... x , где x -e представляют биты справа от 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) на единицу, но это невозможно.

7 голосов
/ 02 августа 2010

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

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

5 голосов
/ 06 марта 2018

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

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

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

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

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

>>> int(float('38.2'))
38
1 голос
/ 08 апреля 2016

Еще один пример кода для преобразования вещественного числа / числа с плавающей точкой в ​​целое число с использованием переменных. «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))
0 голосов
/ 21 сентября 2018

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

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

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. Таким образом, значение останется с плавающей запятой, и вы не потеряете никакой точности.

...