如何找到整数中数字的长度?

375
在Python中,如何找到一个整数的数字个数?
```

在Python中,如何找到一个整数的数字个数?

```

4
我不理解你的问题。你是指整数的大小吗?你想找出数字的位数吗?请澄清。 - batbrat
32个回答

4

为了记录,毫无疑问这是解决这个问题最慢的方法:

def num_digits(num, number_of_calls=1):
    "Returns the number of digits of an integer num."
    if num == 0 or num == -1:
        return 1 if number_of_calls == 1 else 0
    else:
        return 1 + num_digits(num/10, number_of_calls+1)

3
如其他答案所示,使用log10会导致大数字n的错误结果,而使用len(str(...))或手动循环会导致大数字n的性能下降。Jodag's answer提供了一个非常好的替代方法,不过它只适用于可能会导致计算机崩溃的整数。但我们可以做得更好,还可以更快(对于小到足以保证math.log2准确的n),避免使用对数并改用二进制:
def num_digits(n: int) -> int:
    assert n > 0
    i = int(0.30102999566398114 * (n.bit_length() - 1)) + 1
    return (10 ** i <= n) + i

让我们来分析一下。首先有一个奇怪的n.bit_length()。这个函数计算二进制长度:

assert 4 == (0b1111).bit_length()
assert 8 == (0b1011_1000).bit_length()
assert 9 == (0b1_1011_1000).bit_length()

与对数不同,这种方法对于整数来说既快又精确。事实证明,这会得到恰好 floor(log2(n)) + 1 的结果。为了单独获得 floor(log2(n)),我们需要减去 1,因此得到 n.bit_length() - 1
接下来,我们乘以 0.30102999566398114。这相当于略微向下舍入的 log10(2)。这利用对数规则从 floor(log2(n)) 计算出 floor(log10(n)) 的估计值。
现在,您可能想知道此时我们可能有多大偏差,因为虽然 0.30102999566398114 * log2(n) ~ log10(n),但对于 floor(0.30102999566398114 * floor(log2(n))) ~ floor(log10(n)) 却不成立。请记住 x - 1 < floor(x) <= x,因此我们可以进行一些快速的数学运算:
log2(n) - 1 < floor(log2(n)) <= log2(n)

log10(n) - 0.30102999566398114 < 0.30102999566398114 * floor(log2(n)) <= log10(n)

floor(log10(n) - 0.30102999566398114) < floor(0.30102999566398114 * floor(log2(n))) <= floor(log10(n))

请注意,floor(log10(n) - 0.30102999566398114) 至少是 floor(log10(n)) - 1,这意味着我们的结果最多只会偏差 1。这就是最终校正的地方,我们检查 10 ** i <= n,如果结果太小,则会得到额外的 1 +,如果结果恰好,则会得到 0 +
类似于Jodag的答案,这种方法对于非常非常大的 n(大约是 10 ** 2 ** 52)会失败,其中 i 的偏差超过了 -1。但是,那么大的整数可能会导致计算机崩溃,因此这应该足够了。

3
from math import log10
digits = lambda n: ((n==0) and 1) or int(log10(abs(n)))+1

2

将科学计数法转换为普通数字并去掉指数:

int("{:.5e}".format(1000000).split("e")[1]) + 1

我不知道速度如何,但它很简单。

请注意小数点后的有效数字位数(在科学计数法中,".5e"中的“5”可能会将小数部分四舍五入到另一个数字。我将其任意设置为较大值,但可以反映出您所知道的最大数字的长度。


2

通过使用以下方法,可以快速地完成整数运算:

len(str(abs(1234567890)))

如何获取绝对值为"1234567890"的字符串长度?

abs 返回不带负号的数字(仅数字的大小),str 将其转换为字符串,len 返回该字符串的长度。

如果您想使其适用于浮点数,可以使用以下任一方法:

# Ignore all after decimal place
len(str(abs(0.1234567890)).split(".")[0])

# Ignore just the decimal place
len(str(abs(0.1234567890)))-1

供将来参考。


我认为,将输入的数字本身截断(例如使用强制类型转换成 int)比截断它的十进制字符串表示更简单: len(str(abs(int(0.1234567890)))) 返回 1。 - David Foerster
不,那样行不通。如果你把0.17转换成整数,你会得到0,而它的长度与0.17的长度是不同的。 - Frogboxe
在第一种情况下,通过截断字符串表示中包括小数点在内的所有内容,您有效地计算了数字的整数部分的长度,这也是我的建议所做的。对于0.17,两种解决方案都返回1。 - David Foerster

2
def length(i):
  return len(str(i))

2
def digits(n)
    count = 0
    if n == 0:
        return 1
    
    if n < 0:
        n *= -1

    while (n >= 10**count):
        count += 1
        n += n%10

    return count

print(digits(25))   # Should print 2
print(digits(144))  # Should print 3
print(digits(1000)) # Should print 4
print(digits(0))    # Should print 1

2
假设您正在询问整数可以存储的最大值,那么该值取决于具体实现。当使用Python时,我建议您不要以这种方式思考。无论如何,Python中可以存储相当大的值。请记住,Python使用鸭子类型!
编辑:在得到澄清之前,我已经给出了我的答案,解释了整数可以存储的最大值。对于数字位数的问题,我同意接受答案提出的方法。没有更多可添加的内容!

2
这里有另一种计算任何数字小数点前位数的方法。
from math import fabs

len(format(fabs(100),".0f"))
Out[102]: 3

len(format(fabs(1e10),".0f"))
Out[165]: 11

len(format(fabs(1235.4576),".0f"))
Out[166]: 4

我进行了一项简要的基准测试,循环10,000次

num     len(str(num))     ----  len(format(fabs(num),".0f")) ---- speed-up
2**1e0  2.179400e-07 sec  ----     8.577000e-07 sec          ---- 0.2541
2**1e1  2.396900e-07 sec  ----     8.668800e-07 sec          ---- 0.2765
2**1e2  9.587700e-07 sec  ----     1.330370e-06 sec          ---- 0.7207
2**1e3  2.321700e-06 sec  ----     1.761305e-05 sec          ---- 0.1318

这是一种速度较慢但更简单的选择。

但是即使使用这种解决方案,也会在9999999999999998处产生错误的结果。

len(format(fabs(9999999999999998),".0f"))
Out[146]: 16
len(format(fabs(9999999999999999),".0f"))
Out[145]: 17

2

这是一种快速的解决方案,它使用了基于“Better way to compute floor of log(n,b) for integers n and b?”的自校正实现的floor(log10(n))

import math

def floor_log(n, b):
    res = math.floor(math.log(n, b))
    c = b**res
    return res + (b*c <= n) - (c > n)

def num_digits(n):
    return 1 if n == 0 else 1 + floor_log(abs(n), 10)

这非常快,只要n < 10**(2**52)(非常非常大),就可以使用。


1
对于您的相关问题,您可能会对我的答案感兴趣,链接如下:https://dev59.com/UHI95IYBdhLWcg3wtwRe#74099536。我的答案使用二进制而非对数,基于相同的理论,并且适用于其他进制。 - Simply Beautiful Art

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接