在Python中将浮点数转换为整数的最安全方法是什么?

279

Python的math模块包含很方便的函数,例如floorceil。这些函数接受一个浮点数并返回其下面或上面最近的整数。但是,这些函数将答案作为浮点数返回。例如:

import math
f=math.floor(2.3)

现在f返回:

2.0

如何安全地从该浮点数中获取整数,而不会出现舍入误差的风险(例如,如果浮点数等于1.99999)或者我应该使用另一个函数?


8
math.floor在Python 2.6中返回浮点数,但在Python 3中返回整数。目前距离初始帖子已经六年了,这个问题可能很少出现。 - sancho.s ReinstateMonicaCellio
然而,NumPy仍然返回浮点数,因此这个问题是合理的。 - Vincenzooo
标题可以改为“在Python中将浮点数向下取整并转换为整数的最安全方法?” - WurmD
9个回答

213
所有可以用浮点数表示的整数都有一个精确的表示。因此,您可以安全地在结果上使用int。只有当您尝试用分母不是2的幂的有理数来表示时,才会出现不精确的表示。
这个工作并不是微不足道的!这是IEEE浮点表示法的一个属性,如果问题中的数字的大小足够小,则int∘floor = ⌊⋅⌋,但可能存在不同的表示,其中int(floor(2.3))可能为1。
引用自Wikipedia
任何绝对值小于或等于2^24的整数都可以在单精度格式中精确表示,并且任何绝对值小于或等于2^53的整数都可以在双精度格式中精确表示。

10
+1 表示赞同深入探讨。你也可以简要解释一下原因:http://en.wikipedia.org/wiki/Floating_point :D+1 表示同意深入探讨。您还可以简要解释一下原因:http://en.wikipedia.org/wiki/Floating_point :D - Gordon Gustafson
在Python 2中,“int”与C语言的“int”相同。在Python 3中,“int”的大小似乎没有限制,https://dev59.com/MmYr5IYBdhLWcg3wR4Ys。 “int”的含义也取决于操作系统和底层硬件。请参见https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models。如果您正在使用C-API进行编程,则必须非常小心地了解平台上“long”和“size_t”的定义。https://docs.python.org/3/c-api/long.html - Juan

154

使用int(你的非整数数字)将解决问题。

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

4
这对于负数不起作用:floor向下舍入,而int向0舍入。 - jochen
1
@jochen 我在 Python 发行版 Canopy 2.7.6 中测试了 int(-2.3) 并得到了预期的 -2。整数可以是负数,与正式的数学定义相同。 - srodriguex
6
我同意,“int(-2.3)”会像你所说的那样给出“-2”,因为它是向“0”舍入,也就是在这种情况下向上舍入。相比之下,原始问题使用了“math.floor”,它总是向下舍入:“math.floor(-2.3)”会得到“-3.0”。 - jochen
2
这并不是真正的问题。OP只想从math.floor的结果中得到一个整数,而这个答案展示了如何将浮点数转换为整数。从math.floor中取出浮点数,然后通过int进行处理,问题就解决了:int(math.floor(2.3)) - JMTyler
4
你有没有看清楚问题?他知道 int() 函数,但他问的是如果输入的是 1.9999 而不是 2.0 会不会有问题。你的回答根本不是答案,完全错过了重点...请重新考虑回答。 - Mayou36
@Mayou36 看来我错过了重点,但这是我当时拥有的知识所写的,就让它存在吧。社区会根据他们认为合适的方式投赞成或反对票。但是,我真的认为它是正确的:int(-999999999980/99999999999) == -9 # True - srodriguex

60

你可以使用round函数。如果不使用第二个参数(表示有效数字的位数),那么你会得到想要的结果。

IDLE输出。

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

32
在Python 2.6中,round函数也会返回一个浮点数。 - Philipp
9
在Python 3.1.2中,round返回一个整数类型的值。 - robert
3
实际上,在Python 3.x中,roundfloor都返回整数。因此,我想这个问题涉及到Python 2.x。 - Philipp
5
也许可以使用 int(round(2.65)) - teewuane
1
为什么 round(6.5) 的结果是 6?它似乎在小数点后面有一个立即的 5(或更大,最多到 9)时,会类似于 ceil() 函数般将浮点数向上取整。为什么在这种情况下,或者任何以六结尾且小数点后面紧跟着 5 的情况下,这种方法都不起作用呢? - candh
显示剩余2条评论

49

结合之前的两个结果,我们有:

int(round(some_float))

这可以可靠地将浮点数转换为整数。


如果您尝试四舍五入一个非常长的浮点数会发生什么?这会至少引发一个异常吗? - Agostino
@kralyk 我的意思是一个代表比正常的 int 类型所能承载的更大数值的 float。在 Python 2 中,是否有一些 float 值,只能使用 long(经过四舍五入后)来表示? - Agostino
@kralyk 你的意思是,回合之后吗?那么,将它们转换为整数会引发异常,还是只是截断它们? - Agostino
@Agostino 不,int()函数根据需要生成intlong - kralyk
@Agostino 我想我们可以。 - kralyk
显示剩余4条评论

20
这并不是微不足道的事情!它是IEEE浮点表示法的一个特性,即如果涉及数字的数量足够小,则int∘floor = ⌊⋅⌋。但是可能存在不同的表示方法,其中int(floor(2.3))可能为1。
这篇文章解释了它在该范围内的工作原理。
在double中,你可以表示32位整数而没有任何问题。不可能存在任何舍入问题。更准确地说,double可以表示2^53和-2^53之间的全部整数。
简短解释:double可以存储最多53位二进制数字。当你需要更多时,数字会在右侧填充零。
因此,53个1是可以在没有填充的情况下存储的最大数字。显然,所有需要较少位数的(整数)数字可以被精确存储。
将111(省略)111(53个1)加一得到100...000(53个零)。正如我们所知道的那样,我们可以存储53位数字,这使得右侧的零填充。
这就是2^53的含义。
更详细地说,我们需要考虑IEEE-754浮点运算的工作方式。
  1 bit    11 / 8     52 / 23      # bits double/single precision
[ sign |  exponent | mantissa ]

根据以下公式计算数字(不包括在此无关紧要的特殊情况):

-1sign × 1.mantissa ×2exponent - bias

其中 bias = 2exponent - 1 - 1,即双精度和单精度分别为1023和127。

知道乘以2X只需将所有位向左移X个位置,很容易看出任何整数必须使小数点右侧的所有位都为零。

除零之外的任何整数在二进制中都具有以下形式:

1x...x 其中 x 表示 MSB(最高有效位)右侧的位。

因为我们排除了零,所以始终会有一个 MSB 的值为1-这就是为什么它没有被存储。为了存储整数,我们必须将其带入上述格式:-1sign × 1.mantissa ×2exponent - bias

这相当于将位数移到小数点处,直到只剩下 MSB 在左侧。小数点右侧的所有位都存储在尾数中。

由此可以看出,除了 MSB 之外,我们最多可以存储52个二进制数字。

因此,所有位都明确存储的最大数字为

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

为此,我们需要设置指数,使十进制点向右移动52位。如果我们将指数增加1,就无法知道小数点左侧的数字。

111(omitted)111x.

按照惯例,应为0。将整个尾数设为零,我们得到以下数字:

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

这是一个1后面跟着53个零的数字,其中52个存储在位数中,1个由于指数而添加。

它表示253,它标志着我们可以准确表示所有整数的边界(负和正)。如果我们想要将253加1,则必须将隐式零(由x表示)设置为1,但这是不可能的。


13

如果您需要将字符串浮点数转换为整数,可以使用此方法。

例如:'38.0' 转换为 38

为了将其转换为整数,您可以将其强制转换为浮点数,然后再转换为整数。这也适用于浮点字符串或整数字符串。

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

注意:这将去除小数点后的任何数字。

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

9

math.floor会始终返回一个整数,因此int(math.floor(some_float))永远不会引入四舍五入误差。

然而,在math.floor(some_large_float)中可能已经引入了舍入误差,甚至在首次将大数字存储为浮点数时也可能出现这种情况(当存储大数字时,浮点数可能会失去精度)。


7
从:http://docs.python.org/2/library/math.html - math.floor(x) - 返回 x 的下取整值,即小于或等于 x 的最大整数值,返回结果为浮点型。 - Bill Rosmus
为什么在int已经能实现相同功能的情况下,还需要调用math.floor函数呢? - Alex
2
@Alex:当然,对于负数,intfloor返回不同的值。 - user3850

2

既然你要求“最安全”的方式,那我提供另一个答案,不同于排名第一的答案。

确保不丢失任何精度的简便方法是在转换后检查这些值是否相等。

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

例如,如果浮点数是1.0,那么1.0等于1。这时转换为整数执行。而如果浮点数是1.1,则int(1.1)等于1,1.1!= 1。因此,该值将保持浮点数,不会丢失任何精度。


2

以下是一个代码示例,使用变量将实数/浮点数转换为整数。 "vel" 是一个实数/浮点数,并转换为最接近的整数,"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))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接