Python 四舍五入到 10 的下一个最高幂

off*_*fel 50 python ceil

我将如何设法将math.ceil一个数字分配给 10 的下一个最高幂?

# 0.04  ->  0.1
# 0.7   ->  1
# 1.1   ->  10  
# 90    ->  100  
# ...
Run Code Online (Sandbox Code Playgroud)

我目前的解决方案是一个检查输入数字范围的字典,但它是硬编码的,我更喜欢单行解决方案。也许我在这里错过了一个简单的数学技巧或相应的 numpy 函数?

jon*_*rpe 68

您可以使用math.ceilwithmath.log10来执行此操作:

>>> 10 ** math.ceil(math.log10(0.04))
0.1
>>> 10 ** math.ceil(math.log10(0.7))
1
>>> 10 ** math.ceil(math.log10(1.1))
10
>>> 10 ** math.ceil(math.log10(90))
100
Run Code Online (Sandbox Code Playgroud)

log10(n)为您提供x满足的解决方案10 ** x == n,因此如果您将x其四舍五入,它将为您提供下一个最高 10 次幂的指数。

注意,对于价值n在那里x已经是一个整数,则“的10次大功率”将是n

>>> 10 ** math.ceil(math.log10(0.1))
0.1
>>> 10 ** math.ceil(math.log10(1))
1
>>> 10 ** math.ceil(math.log10(10))
10
Run Code Online (Sandbox Code Playgroud)

  • 注意:这个答案依赖于浮点运算,因此它可能会由于舍入错误而失败。例如,尝试输入 1000000000000001。 (7认同)
  • 是的,您可以传递其他类型,但它们将转换为双精度浮点数并传递给 C“log10”函数。有一种特殊情况可以防止大数日志溢出,但无法防止舍入错误。 (6认同)
  • 注意:根据您想要的行为,这不适用于 10 的幂,例如 `10 ** math.ceil(math.log10(1)) == 1`,这不是“下一个最高幂” (2认同)
  • @plugwash不一定,数学函数也将接受例如decimal.Decimals。 (2认同)

plu*_*ash 23

您的问题未详细说明,您需要退后一步并提出一些问题。

  • 您的输入是什么类型?
  • 您想要什么类型的输出?
  • 对于小于 1 的结果,您究竟想四舍五入到什么?您想要 10 的实际幂还是 10 的幂的浮点近似值?您知道 10 的负幂不能准确地用浮点数表示吗?现在让我们假设您想要 10 的幂的浮点近似值。
  • 如果输入正好是 10 的幂(或最接近的 10 幂的浮点近似值),输出是否应该与输入相同?还是应该是 10 的下一个幂?“10 -> 10”还是“10 -> 100”?让我们暂时假设前者。
  • 您的输入值可以是相关类型的任何可能值吗?或者他们更受限制。

在另一个答案中,建议取对数,然后向上取整(天花板函数),然后取幂。

def nextpow10(n):
    return 10 ** math.ceil(math.log10(n))
Run Code Online (Sandbox Code Playgroud)

不幸的是,这会导致舍入错误。首先 n 从它碰巧拥有的任何数据类型转换为双精度浮点数,可能会引入舍入误差,然后计算对数可能会在其内部计算和结果中引入更多的舍入误差。

因此,我很快就找到了一个给出错误结果的例子。

>>> import math
>>> from numpy import nextafter
>>> n = 1
>>> while (10 ** math.ceil(math.log10(nextafter(n,math.inf)))) > n:
...     n *= 10
... 
>>> n
10
>>> nextafter(n,math.inf)
10.000000000000002
>>> 10 ** math.ceil(math.log10(10.000000000000002))
10
Run Code Online (Sandbox Code Playgroud)

从理论上讲,它也有可能在另一个方向上失败,尽管这似乎更难激发。

因此,对于浮点数和整数的稳健解决方案,我们需要假设对数的值只是近似值,因此我们必须测试几种可能性。类似的东西

def nextpow10(n):
    p = round(math.log10(n))
    r = 10 ** p
    if r < n:
        r = 10 ** (p+1) 
    return r;
Run Code Online (Sandbox Code Playgroud)

我相信这段代码应该在合理的现实世界范围内为所有参数提供正确的结果。由于将它们转换为浮点数的问题,它会因非常小或非常大的非整数和非浮点类型而中断。Python 特殊情况下 log10 函数的整数参数试图防止溢出,但仍然有足够大的整数,由于舍入错误,可能会强制产生不正确的结果。

为了测试这两个实现,我使用了以下测试程序。

n = -323 # 10**-324 == 0
while n < 1000:
    v = 10 ** n
    if v != nextpow10(v): print(str(v)+" bad")
    try:
        v = min(nextafter(v,math.inf),v+1)
    except:
        v += 1
    if v > nextpow10(v): print(str(v)+" bad")
    n += 1
Run Code Online (Sandbox Code Playgroud)

这在幼稚的实现中发现了很多失败,但在改进的实现中没有。