Python达到10的下一个最高幂


44

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

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

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


3
@bold看起来好像那些解决方案都可以正常工作10,这需要使用eg log10
jonrsharpe

3
您想要的词是“力量”。可能是因为您的母语单词翻译错误。
user2357112支持Monica

谢谢,莫妮卡!@bold:我发现了这个问题,但这是一个不同的问题。Jonrsharpe提供了一个完美的答案
offeltoffel

2
这也与数量级有关。1是0阶,10是1阶,100是2阶,
依此类推

Answers:


60

您可以使用math.ceilwith math.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)为您提供了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

1
使用log函数似乎只是我无法想到的技巧。我相信这正是我所希望的!非常感谢
offeltoffel

2
注意:根据您的期望行为,这不适用于10的幂,例如10 ** math.ceil(math.log10(1)) == 1,它不是“第二高的幂”
Cireo

5
注意:此答案依赖于浮点运算,因此由于舍入错误而可能失败。例如,尝试输入1000000000000001。
plugwash

2
@plugwash不一定,数学函数也将接受例如decimal.Decimals。
jonrsharpe

5
是的,您可以传递其他类型,但是它们将被转换为双精度浮点数并传递给C“ log10”函数。有一种特殊情况可以防止大量日志溢出,但是没有什么可以防止舍入错误。
plugwash

21

您的问题未指定,您需要退后一步并提出一些问题。

  • 您输入的是什么类型?
  • 您想要什么类型的输出?
  • 对于小于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的答案已经解决了我的问题,但该答案对于其他有类似但更具体问题的人可能很有用。
offeltoffel

1
为什么用round代替math.ceil?这将引入很多不必要的情况,这r < n是正确的,因此需要执行其他工作。
a_guest

1
因为日志可能在任一方向均关闭。
plugwash

1
ceil会导致低端1e-317和100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
plugwash

1
(实际上它可能是罚款虽然)
plugwash

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))

刚刚测试了这个,我给它一个赞誉,因为它在大多数实际情况下似乎都很好用,但是在输入足够小的情况下,它确实遭受舍入误差的困扰。ceiling10(1E-6)给出1.0000000000000002e-06
plugwash

0
y = math.ceil(x)
z = y + (10 - (y % 10))

像这样的东西?它只是在我的头顶上,但是当我在终端中尝试几个数字时它就起作用了。


0

看一下这个!

>>> 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 ) > 1

    Float数字-转换为int,其后为字符串str(),将为我们提供一个正好stringlength我们一起寻找的对象。因此,第一部分,对于输入i > 1.0-它是10此长度的十次方。

    1. &3.小分支:i > 1.0i > 0.1<=> 分别是101
    1. 最后一种情况,当i < 0.1:此处,十将为负数。为了获得逗号后的第一个非零元素,我使用了这样的构造("%.100f" % i ).replace('.','').index( k ),其中k在[1:10]间隔内运行。此后,取最小的结果列表。减一,它是第一个零,应计数。另外,index()如果标准python 无法从[1:10]interval中找到非零元素中的至少一个,则标准python 可能会崩溃,这就是为什么最后我必须通过出现来“过滤”列表的原因if str( j ) in "%.100f" % i。此外,要更深入地了解- %.100f可能会有所不同。

请添加一些解释。
Mobin Ranjbar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.