在python中将浮点数转换为整数的最安全方法?

Posted

技术标签:

【中文标题】在python中将浮点数转换为整数的最安全方法?【英文标题】:Safest way to convert float to integer in python? 【发布时间】:2011-03-24 04:37:28 【问题描述】:

Python 的数学模块包含方便的函数,例如 floorceil。这些函数采用浮点数并返回其下方或上方最接近的整数。然而,这些函数将答案作为浮点数返回。例如:

import math
f=math.floor(2.3)

现在f 返回:

2.0

什么是从这个浮点数中获取整数的最安全方法,而不会有舍入错误的风险(例如,如果浮点数等于 1.99999)或者我应该完全使用另一个函数?

【问题讨论】:

math.floor returns a float in v2.6,但it returns an integer in v3。在这一点上(在 OP 之后将近六年),这个问题可能很少出现。 但是numpy仍然返回float,所以这个问题是有效的。 标题可以改进为“在python中四舍五入并将浮点数转换为整数的最安全方法?” 【参考方案1】:

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

不过,math.floor(some_large_float) 中可能已经引入了舍入误差,或者甚至在最初将大量数字存储在浮点数中时。 (存储在浮点数中时,大数字可能会丢失精度。)

【讨论】:

来自:docs.python.org/2/library/math.html - math.floor(x) - 以浮点数形式返回 x 的下限,小于等于 x 的最大整数值。 当 int 已经做了同样的事情时,为什么还需要调用 math.floor? @Alex: intfloor 当然会为负数返回不同的值。【参考方案2】:

所有可以用浮点数表示的整数都有一个精确的表示。所以你可以安全地在结果上使用int。仅当您尝试使用不是 2 的幂的分母来表示有理数时,才会出现不精确的表示。

这个工作一点都不简单!如果所讨论的数字的大小足够小,则 int∘floor = ⌊⋅⌋ 是 IEEE 浮点表示的一个属性,但 int(floor(2.3)) 可能为 1 时可能会有不同的表示。

引用Wikipedia,

任何绝对值小于等于2的整数24都可以用单精度格式精确表示,任何绝对值小于等于2的整数53 sup> 可以用双精度格式精确表示。

【讨论】:

+1 表示更深入。你也可以简单解释一下原因:en.wikipedia.org/wiki/Floating_point:D 在 Python 2 中,“int”与 C 中的“int”相同。在 Python 3 中,“int,***.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【参考方案3】:

你可以使用round函数。如果你不使用第二个参数(有效数字的#),那么我认为你会得到你想要的行为。

空闲输出。

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

【讨论】:

round 也返回一个浮点数,至少在 Python 2.6 中是这样。 在 Python 3.1.2 中,round 返回一个 int。 确实,roundfloor 在 Python 3.x 中都返回整数。所以我想这个问题涉及 Python 2.x。 也许int(round(2.65)) ? 为什么round(6.5) 给出6?在所有其他情况下,当小数点后立即有 5(或大于 9)时,这似乎有点像 ceil() 浮点数。为什么这在这种情况下不起作用?或任何其他数字以六结尾并且小数点后有一个 5 的情况......【参考方案4】:

使用int(your non integer number) 会搞定的。

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

【讨论】:

这不适用于负数:floor 向下舍入,而 int 向 0 舍入。 @jochen 我在 Python 发行版 Canopy 2.7.6 中测试了int(-2.3),并得到了预期的-2。整数可以为负数,与正式数学定义中的方式相同。 我同意,int(-2.3) 就像你说的那样给出-2,因为它向0 舍入,在这种情况下向上。相比之下,原始问题使用math.floor,它总是向下取整:math.floor(-2.3) 给出-3.0 这不是问题。 OP 只想要math.floor 的结果中的一个整数,这个答案显示了如何将浮点数转换为整数。从math.floor 获取浮点数并将其通过int 管道,问题已解决:int(math.floor(2.3)) 你读过这个问题吗?他知道 int() 函数,但询问您是否会在使用 1.9999 而不是 2.0 时遇到问题。你的答案根本不是一个答案,你错过了整点......【参考方案5】:

这很有效!如果所讨论的数字的大小足够小,则 int∘floor = ⌊⋅⌋ 是 IEEE 浮点表示的一个属性,但 int(floor(2.3)) 可能为 1 时可能会有不同的表示。

这篇文章解释了为什么它在这个范围内工作

在双精度中,您可以毫无问题地表示 32 位整数。 不可能有任何舍入问题。更准确地说,双精度可以表示 253-2 之间的所有整数53.

简短说明:一个 double 最多可以存储 53 个二进制数字。当您需要更多时,数字会在右侧用零填充。

因此 53 是可以存储的最大数字而无需填充。自然,所有需要较少位数的(整数)数字都可以准确存储。

111(省略)111(53 个一)加一得到 100...000,(53 个零)。我们知道,我们可以存储 53 位数字,这使得最右边的零填充。

这就是 253 的来源。


更多细节:我们需要考虑 IEEE-754 浮点的工作原理。

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

然后按如下方式计算数量(不包括此处不相关的特殊情况):

-1符号 × 1.mantissa ×2指数 - 偏差

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

知道乘以 2X 只是将所有位 X 位向左移动,很容易看出任何整数必须具有所有尾数中从小数点右边到零的位。

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

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

因为我们排除了零,所以总是有一个 MSB 为一,这就是它不被存储的原因。为了存储整数,我们必须把它变成上述形式:-1sign × 1.mantissa ×2exponent - bias。 p>

这与将位移动到小数点后的含义相同,直到 MSB 左侧只有 MSB。然后将小数点右边的所有位存储在尾数中。

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

由此可见,显式存储所有位的最大数是

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 由于指数而添加。

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

【讨论】:

【参考方案6】:

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

int(round(some_float))

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

【讨论】:

如果你尝试舍入一个很长的浮点数会发生什么?这至少会引发异常吗? @Agostino “非常长的浮动”是什么意思? @kralyk 我的意思是float 代表的数字大于普通int 可以容纳的数字。在 Python 2 中,是否存在只能使用 long(舍入后)表示的 float 值? @kralyk 你的意思是,在一轮之后?那么,将它们强制转换为 int 会引发异常,还是截断它们? @Agostino 不,int() 函数根据需要生成intlong...【参考方案7】:

另一个使用变量将实数/浮点数转换为整数的代码示例。 “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))

【讨论】:

【参考方案8】:

如果需要将字符串float转成int,可以使用这个方法。

示例:'38.0'38

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

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

注意:这将去除小数点后的所有数字。

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

【讨论】:

【参考方案9】:

由于您要求“最安全”的方式,我将提供除最佳答案之外的另一个答案。

确保不丢失任何精度的一种简单方法是检查转换后的值是否相等。

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。因此该值将保持为浮点数,并且不会丢失任何精度。

【讨论】:

以上是关于在python中将浮点数转换为整数的最安全方法?的主要内容,如果未能解决你的问题,请参考以下文章

将两个整数x和y有效地转换为浮点数x.y

在 Haskell 中将字符串转换为整数/浮点数?

如何在python中将23位浮点数从字符串转换为浮点数并返回?

无法在python中将字符串转换为浮点数

Python:将 2 个整数转换为浮点数

FORTRAN里怎样把数值类型(整数,浮点数)转换为字符串