简体   繁体   English

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

[英]Python round to next highest power of 10

How would I manage to perform math.ceil such that a number is assigned to the next highest power of 10?我将如何设法执行math.ceil以便将数字分配给 10 的下一个最高幂?

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

My current solution is a dictionary that checks the range of the input number, but it's hardcoded and I would prefer a one-liner solution.我当前的解决方案是检查输入数字范围的字典,但它是硬编码的,我更喜欢单行解决方案。 Maybe I am missing a simple mathematical trick or a corresponding numpy function here?也许我在这里缺少一个简单的数学技巧或相应的 numpy function ?

You can use math.ceil with math.log10 to do this:您可以使用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) gives you the solution x that satisfies 10 ** x == n , so if you round up x it gives you the exponent for the next highest power of 10. log10(n)为您提供满足10 ** x == n的解决方案x ,因此如果您将x向上取整,它会为您提供下一个最高 10 次幂的指数。

Note that for a value n where x is already an integer, the "next highest power of 10" will be n :请注意,对于x已经是 integer 的值n“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

Your problem is under-specified, you need to step back and ask some questions.您的问题未明确说明,您需要退后一步并提出一些问题。

  • What type(s) are your inputs?您的输入是什么类型?
  • What type(s) do you want for your outputs?你想要什么类型的输出?
  • For results less than 1, what exactly do you want to round to?对于小于 1 的结果,您究竟想要四舍五入到什么? Do you want actual powers of 10 or floating point approximations of powers of 10?您想要 10 的实际幂还是 10 的幂的浮点近似值? You are aware that negative powers of 10 can't be expressed exactly in floating point right?您知道 10 的负幂不能精确地用浮点数表示吗? Let's assume for now that you want floating point approximations of powers of 10.现在让我们假设您想要 10 次幂的浮点近似值。
  • If the input is exactly a power of 10 (or the closest floating point approximation of a power of 10), should the output be the same as the input?如果输入恰好是 10 的幂(或 10 的幂的最接近浮点近似值),output 是否应该与输入相同? Or should it be the next power of 10 up?还是应该是 10 的下一个幂? "10 -> 10" or "10 -> 100"? “10 -> 10”还是“10 -> 100”? Let's assume the former for now.我们现在假设前者。
  • Can your input values be any possible value of the types in question?您的输入值可以是所讨论类型的任何可能值吗? or are they more constrained.还是他们更受限制。

In another answer it was proposed to take the logarithm, then round up (ceiling function), then exponentiate.在另一个答案中,建议取对数,然后四舍五入(天花板函数),然后取幂。

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

Unfortunately this suffers from rounding errors.不幸的是,这会受到舍入误差的影响。 First of all n is converted from whatever data type it happens to have into a double precision floating point number, potentially introducing rounding errors, then the logarithm is calculated potentially introducing more rounding errors both in its internal calculations and in its result.首先,n 从它碰巧拥有的任何数据类型转换为双精度浮点数,可能会引入舍入误差,然后计算对数可能会在其内部计算和结果中引入更多舍入误差。

As such it did not take me long to find an example where it gave an incorrect result.因此,我很快就找到了一个给出错误结果的例子。

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

It is also theoretically possible for it to fail in the other direction, though this seems to be much harder to provoke.从理论上讲,它也有可能在另一个方向上失败,尽管这似乎更难激怒。

So for a robust solution for floats and ints we need to assume that the value of our logarithm is only approximate, and we must therefore test a couple of possibilities.因此,对于浮点数和整数的稳健解决方案,我们需要假设我们的对数值只是近似值,因此我们必须测试几种可能性。 Something along the lines of类似的东西

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

I believe this code should give correct results for all arguments in a sensible real-world range of magnitudes.我相信这段代码应该为所有 arguments 在合理的真实世界范围内给出正确的结果。 It will break for very small or very large numbers of non integer and non-floating point types because of issues converting them to floating point.由于将它们转换为浮点的问题,它会破坏非常少量或非常大量的非 integer 和非浮点类型。 Python special cases integer arguments to the log10 function in an attempt to prevent overflow, but still with a sufficiently massive integer it may be possible to force incorrect results due to rounding errors. Python special cases integer arguments to the log10 function in an attempt to prevent overflow, but still with a sufficiently massive integer it may be possible to force incorrect results due to rounding errors.

To test the two implementations I used the following test program.为了测试这两个实现,我使用了以下测试程序。

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

This finds lots of failures in the naive implementation, but none in the improved implementation.这在幼稚的实现中发现了很多失败,但在改进的实现中却没有。

It seems you want rather the lowest next power of 10... Here is a way using pure maths and no log, but recursion.看来您想要的是 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))

Check this out!看一下这个!

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

This code based on principle of ten's power in len(str(int(float_number))) .此代码基于len(str(int(float_number)))中的十次方原理。

There are 4 cases:有4种情况:

    1. int(i) > 1 . int(i) > 1

      Float number - converted to int , thereafter string str() from it, will give us a string with length which is we are looking exactly. Float - 转换为int ,然后字符串str()将给我们一个length为我们正在查看的string So, first part, for input i > 1.0 - it is ten 10 in power of this length.所以,第一部分,对于输入i > 1.0 - 它是这个长度的 10 的10次方。

    1. & 3. Little branching: i > 1.0 and i > 0.1 <=> it is 10 and 1 respectively. & 3. 小分支: i > 1.0i > 0.1 <=> 分别是101
    1. And last case, when i < 0.1 : Here, ten shall be in negative power.最后一种情况,当i < 0.1时:这里,10 应该是负幂。 To get first non zero element after comma, I've used such construction ("%.100f" % i).replace('.', '').index(k) , where k run over [1:10] interval.为了在逗号后获得第一个非零元素,我使用了这样的构造("%.100f" % i).replace('.', '').index(k) ,其中 k 超过[1:10]间隔. Thereafter, take minimum of result list.此后,取最少的结果列表。 And decrease by one, it is first zero, which shall be counted.而减一,则第一个为零,应计入。 Also, here standard python's index() may crash, if it will not find at least one of non-zero element from [1:10] interval, that is why in the end I must "filter" listing by occurrence: if str(j) in "%.100f" % i .此外,这里标准 python 的index()可能会崩溃,如果它不会从[1:10]间隔中找到至少一个非零元素,这就是为什么最后我必须按出现“过滤”列表: if str(j) in "%.100f" % i中。 Additionally, to get deeper precise - %.100f may be taken differ.此外,为了获得更深的精确度 - %.100f可能会有所不同。

I think the simplest way is:我认为最简单的方法是:

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

I hope this help you.我希望这对你有帮助。

a specific shortcut works for big-integers that are already coming in as string-format:一个特定的快捷方式适用于已经以字符串格式出现的大整数:

instead of having to first convert it to integer, or running it through the log()/ceiling() function, or perform any sort of modulo math, the next largest power-of-10 is simply:不必先将其转换为 integer,或通过log()/ceiling() function 运行它,或执行任何类型的模数运算,下一个最大的 10 次幂很简单:

 10 ** length(big_int_str_var)

—- below: 1st one generates a string formatted power-of-10, the 2nd one is numeric --- 下面:第一个生成一个字符串格式的 10 次幂,第二个是数字

echo 23958699683561808518065081866850688652086158016508618152865101851111111111111 | tee >( gpaste | gcat -n >&2; ) | gcat - | python3 -c '\ import sys; [ print("1"+"0"*len(_.strip("\n"))) for _ in sys.stdin ]' or '... [ print( 10 ** len(_.strip("\n"))) for _ in sys.stdin ]'
1 23958699683561808518065081866850688652086158016508618152865101851111111111111

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

Something like this maybe?可能是这样的? It's just off the top of my head but it worked when I tried a few numbers in terminal.这只是我的想法,但是当我在终端中尝试了几个数字时它就起作用了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM