【问题标题】:Python round to next highest power of 10Python四舍五入到下一个最高的10次幂
【发布时间】:2020-04-03 09:35:07
【问题描述】:

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

# 0.04  ->  0.1
# 0.7   ->  1
# 1.1   ->  10  
# 90    ->  100  
# ...

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

【问题讨论】:

  • @bold 看起来这些解决方案从10 开始工作,这需要一些东西,例如log10.
  • 你想要的词是“权力”。也许你的母语单词翻译错了。
  • 谢谢,莫妮卡! @bold:我发现了这个问题,但这是一个不同的问题。 Jonrsharpe 提供了一个完美的答案
  • 这也和order of magnitude有关。 1 是 0 阶,10 是 1 阶,100 是 2 阶,依此类推。

标签: python ceil


【解决方案1】:

您可以使用math.ceilmath.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

log10(n) 为您提供满足 10 ** x == n 的解决方案 x,因此如果您将 x 向上取整,它会为您提供 10 的下一个最高幂的指数。

注意对于 n 的值,其中 x 已经是一个整数,“10 的下一个最高幂”将是 n: p>

>>> 10 ** math.ceil(math.log10(0.1))
0.1
>>> 10 ** math.ceil(math.log10(1))
1
>>> 10 ** math.ceil(math.log10(10))
10

【讨论】:

  • 使用日志功能似乎只是我想不出的技巧。我相信这正是我所希望的!非常感谢
  • 注意:根据您想要的行为,这不适用于 10 的幂,例如10 ** math.ceil(math.log10(1)) == 1,这不是“下一个最高权力”
  • 注意:这个答案依赖于浮点运算,因此它可能由于舍入错误而失败。例如,尝试输入 1000000000000001。
  • @plugwash 不一定,数学函数也将接受例如小数。小数。
  • 是的,您可以传递其他类型,但它们将被转换为双精度浮点数并传递给C“log10”函数。有一种特殊情况可以防止大量日志溢出,但没有什么可以防止舍入错误。
【解决方案2】:

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

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

在另一个答案中,建议取对数,然后四舍五入(天花板函数),然后取幂。

def nextpow10(n):
    return 10 ** math.ceil(math.log10(n))

不幸的是,这会出现舍入错误。首先,将 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

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

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

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

我相信这段代码应该在一个合理的真实世界范围内为所有参数给出正确的结果。由于将它们转换为浮点的问题,它会因非常少或非常多的非整数和非浮点类型而中断。 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

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

【讨论】:

  • 感谢您在此处详细介绍。虽然 jonrsharpe 的答案已经解决了我的问题,但这个答案可能对其他有类似但更具体问题的人有用。
  • 你为什么用round而不是math.ceil?这将引入许多不必要的情况,其中r &lt; n 为真,因此需要执行额外的工作。
  • 因为日志可能在任一方向关闭。
【解决方案3】:

您似乎想要 10 的最低次幂... 这是一种使用纯数学且没有日志但使用递归的方法。

def ceiling10(x):
    if (x > 10):
        return ceiling10(x / 10) * 10
    else:
        if (x <= 1):
            return ceiling10(10 * x) / 10
        else:
            return 10
for x in [1 / 1235, 0.5, 1, 3, 10, 125, 12345]:
    print(x, ceiling10(x))

【讨论】:

    【解决方案4】:

    看看这个!

    >>> i = 0.04123
    >>> print i, 10 ** len(str(int(i))) if int(i) > 1 else 10 if i > 1.0 else 1 if i > 0.1 else 10 ** (1 - min([("%.100f" % i).replace('.', '').index(k) for k in [str(j) for j in xrange(1, 10) if str(j) in "%.100f" % i]]))               
    0.04123 0.1
    >>> i = 0.712
    >>> print i, 10 ** len(str(int(i))) if int(i) > 1 else 10 if i > 1.0 else 1 if i > 0.1 else 10 ** (1 - min([("%.100f" % i).replace('.', '').index(k) for k in [str(j) for j in xrange(1, 10) if str(j) in "%.100f" % i]]))                 
    0.712 1
    >>> i = 1.1
    >>> print i, 10 ** len(str(int(i))) if int(i) > 1 else 10 if i > 1.0 else 1 if i > 0.1 else 10 ** (1 - min([("%.100f" % i).replace('.', '').index(k) for k in [str(j) for j in xrange(1, 10) if str(j) in "%.100f" % i]]))                   
    1.1 10
    >>> i = 90
    >>> print i, 10 ** len(str(int(i))) if int(i) > 1 else 10 if i > 1.0 else 1 if i > 0.1 else 10 ** (1 - min([("%.100f" % i).replace('.', '').index(k) for k in [str(j) for j in xrange(1, 10) if str(j) in "%.100f" % i]]))                    
    90 100
    

    此代码基于len(str(int(float_number)))中的十次方原理。

    有4种情况:

      1. int(i) &gt; 1.

        Float 数字 - 转换为 int,然后从它的字符串 str() 将给我们一个 stringlength,这是我们正在寻找的准确。所以,第一部分,对于输入 i &gt; 1.0 - 这个长度的幂是十个 10

      1. & 3. 小分支:i &gt; 1.0i &gt; 0.1 分别是 101
      1. 最后一种情况,当i &lt; 0.1:这里,十应该是负幂。为了在逗号之后获得第一个非零元素,我使用了这样的构造 ("%.100f" % i).replace('.', '').index(k),其中 k 超过了 [1:10] 间隔。此后,取最少的结果列表。减一,则第一个为零,应计入。此外,这里标准 python 的index() 可能会崩溃,如果它无法从[1:10] 间隔中找到至少一个非零元素,这就是为什么最后我必须按出现“过滤”列表:if str(j) in "%.100f" % i。 此外,为了获得更精确的信息 - %.100f 可能会有所不同。

    【讨论】:

    • 请添加一些解释。
    【解决方案5】:

    我认为最简单的方法是:

    import math
    
    
    number = int(input('Enter a number: '))
    next_pow_ten = round(10 ** math.ceil(math.log10(number)))
    print(str(10) + ' power ' + str(round(math.log10(number))) + ' = '\
          + str(next_pow_ten))
    

    希望对你有所帮助。

    【讨论】:

      【解决方案6】:
      y = math.ceil(x)
      z = y + (10 - (y % 10))
      

      可能是这样的?这只是我的想法,但是当我在终端中尝试了几个数字时它就起作用了。

      【讨论】:

      • 这会四舍五入到 10 的下一个倍数,但问题是要四舍五入到 10 的下一个幂。例如。 42 应该四舍五入到 100,1234 应该四舍五入到 10000。
      猜你喜欢
      • 1970-01-01
      • 2010-10-02
      • 1970-01-01
      • 2011-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-21
      • 1970-01-01
      相关资源
      最近更新 更多