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

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

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

```

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

486
如果你想知道整数的长度,即整数中数字的数量,你可以将其转换为字符串,例如str(133),然后像len(str(123))那样找到它的长度。

33
当然,如果你想要查找数字的位数,这将产生一个对于负数来说过大的结果,因为它会计算负号。 - Chris Upchurch
86
嘿,这是一个慢速的解决方案。我计算了一个随机六位数的阶乘并求出它的长度。这种方法花费了95.891秒。而Math.log10方法仅需7.486343383789062e-05秒,大约快了1501388倍! - FadedCoder
9
不仅速度慢,而且消耗的内存更多,在处理大量数据时可能会出问题。请使用 Math.log10 替代。 - Peyman
len(str(0)) 是 1。 - Sridhar Thiagarajan
7
‘0’ 的数字位数不是等于1吗? - GeekTantra
4
使用 abs() 函数来去除负号。例如:len(str(abs(-123))) == 3 - Neil

381

不需要转换为字符串

import math
digits = int(math.log10(n))+1

为了处理零和负数,可以采取以下方法:
import math
if n > 0:
    digits = int(math.log10(n))+1
elif n == 0:
    digits = 1
else:
    digits = int(math.log10(-n))+2 # +1 if you don't count the '-' 

你可能想把它放在一个函数里 :)

以下是一些基准测试。即使对于很小的数字,len(str())也已经落后了。

timeit math.log10(2**8)
1000000 loops, best of 3: 746 ns per loop
timeit len(str(2**8))
1000000 loops, best of 3: 1.1 µs per loop

timeit math.log10(2**100)
1000000 loops, best of 3: 775 ns per loop
 timeit len(str(2**100))
100000 loops, best of 3: 3.2 µs per loop

timeit math.log10(2**10000)
1000000 loops, best of 3: 844 ns per loop
timeit len(str(2**10000))
100 loops, best of 3: 10.3 ms per loop

14
使用log10是一个数学家的解决方案;而使用len(str())则是程序员的解决方案,更加清晰简单。 - Glenn Maynard
102
@Glenn:我当然希望你没有暗示这是一个糟糕的解决方案。程序员天真的O(log10 n)解决方案在即席、原型代码中表现良好——但我更愿意在生产代码或公共API中看到数学家优雅的O(1)解决方案。赞同gnibbler的观点,加一。 - Juliet
19
嗨!我遇到一些奇怪的问题,有人能解释一下为什么对于 99999999999999999999999999999999999999999999999999999999999999999999999 (71个数字9),int(math.log10(x)) +1 返回 72 吗? 我以为我可以依赖log10方法,但实际上我必须使用len(str(x))代替 :( - Marecky
9
我认为我知道奇怪行为的原因,这是由于浮点数精度不准确造成的,例如math.log10(999999999999999)等于14.999999999999998,所以int(math.log10(999999999999999))变成了14。但当math.log10(9999999999999999)等于16.0。也许使用round函数是解决此问题的方法。 - jamylak
12
经过更多的测试:在10**12以下,len(str(n))是最快的。在此之上,普通的log10始终是最快的,但超过10**15后,它就不正确了。只有在大约10**100的时候,我的解决方案(带有10**b检查的~log10)才能胜过len(str(n))。总之,**请使用len(str(n))**! - gengkev
显示剩余15条评论

65

所有math.log10的解决方案都会给你带来问题。

当您的数字大于999999999999997时,math.log10速度很快,但会出现问题。这是因为浮点数有太多.9,导致结果四舍五入。

因此,为了获得最佳性能,请对较小的数字使用math.log,并仅在超出math.log处理范围时使用len(str())

def getIntegerPlaces(theNumber):
    if theNumber <= 999999999999997:
        return int(math.log10(theNumber)) + 1
    else:
        return len(str(theNumber))

1
那么 len(str(num)) 会更好。 - vighnesh153
2
@Vighnesh Raut:速度慢了很多。 - javapyscript
1
“依赖浮点运算得到精确结果是危险的” - Mark Dickinson,Python核心开发团队成员 https://bugs.python.org/issue3724 - Sreeragh A R
1
@ChaitanyaBangera "And magnitudes slower" - 你在那里拼错了“更快”。(链接已提供) - Kelly Bundy
3
天啊,那太低效了。怎么才得60分? 它比字符串转换慢了几个数量级。 对于一个1040598位数(2 ** 3456789),len(str(number)) 大约需要12秒钟。 我忘记了这个后台任务好几分钟,但它还是没有完成。 - itsTyrion
显示剩余2条评论

44

这个问题被提出已经几年了,但是我编制了一个基准测试,比较了几种计算整数长度的方法。

def libc_size(i): 
    return libc.snprintf(buf, 100, c_char_p(b'%i'), i) # equivalent to `return snprintf(buf, 100, "%i", i);`

def str_size(i):
    return len(str(i)) # Length of `i` as a string

def math_size(i):
    return 1 + math.floor(math.log10(i)) # 1 + floor of log10 of i

def exp_size(i):
    return int("{:.5e}".format(i).split("e")[1]) + 1 # e.g. `1e10` -> `10` + 1 -> 11

def mod_size(i):
    return len("%i" % i) # Uses string modulo instead of str(i)

def fmt_size(i):
    return len("{0}".format(i)) # Same as above but str.format

(libc函数需要一些设置,我没有包含在内)

size_exp感谢Brian Preslopsky,size_str感谢GeekTantra,size_math感谢John La Rooy

以下是结果:

Time for libc size:      1.2204 μs
Time for string size:    309.41 ns
Time for math size:      329.54 ns
Time for exp size:       1.4902 μs
Time for mod size:       249.36 ns
Time for fmt size:       336.63 ns
In order of speed (fastest first):
+ mod_size (1.000000x)
+ str_size (1.240835x)
+ math_size (1.321577x)
+ fmt_size (1.350007x)
+ libc_size (4.894290x)
+ exp_size (5.976219x)
(免责声明:该函数在输入1至1,000,000的范围内运行)
以下是sys.maxsize - 100000sys.maxsize的结果:
Time for libc size:      1.4686 μs
Time for string size:    395.76 ns
Time for math size:      485.94 ns
Time for exp size:       1.6826 μs
Time for mod size:       364.25 ns
Time for fmt size:       453.06 ns
In order of speed (fastest first):
+ mod_size (1.000000x)
+ str_size (1.086498x)
+ fmt_size (1.243817x)
+ math_size (1.334066x)
+ libc_size (4.031780x)
+ exp_size (4.619188x)

正如您所看到的,mod_sizelen("%i" % i))是最快的,略快于使用str(i),而其他方法则慢得多。


1
你真的应该包含libc设置,libc = ctyle.CDLL('libc.so.6', use_errno=True)(猜测是这个)。而且对于大于sys.maxsize的数字不起作用,因为浮点数不能是“非常大”的。所以任何超过这个范围的数字,我想你只能使用较慢的方法了。 - Torxed
有人能将 len("%i" % i) 翻译成 C++ 吗?我想在 C++ 中测试它。 - Tom Tom

28

Python 2.*中的int类型占用4或8个字节(32或64位),具体取决于你使用的Python版本。通过sys.maxint(对于32位整数是2**31-1,对于64位整数是2**63-1),你可以确定使用的是哪一种。

在Python 3中,int类型(类似于Python 2中的long)可以占用任意大小的内存空间,只受可用内存的限制;sys.getsizeof可以给出任何给定值所占用的大致内存大小,但它也会计算一些固定开销:

>>> import sys
>>> sys.getsizeof(0)
12
>>> sys.getsizeof(2**99)
28

如果,正如其他答案所建议的,你想要某个整数值的字符串表示形式,那么只需取该表示形式的len值,无论是十进制还是其他进制!


1
抱歉,这个回答被扣分了。它提供了有用的信息并且回答了问题的可行点(如果只是更具体地说明了想要哪个“len”)。+1 - mjv
3
这看起来很有趣,但不确定如何提取长度。 - Tjorriemorrie

16

假设数字为n,则n的位数由以下公式给出:

math.floor(math.log10(n))+1
请注意,这将为小于10e15的正整数提供正确的答案。超出该范围,math.log10返回类型的精度限制会生效,答案可能会偏离1。在此之后,我建议使用len(str(n));这需要O(log(n))时间,与迭代10的幂相同。
感谢@SetiVolkylany提醒我这个限制。令人惊讶的是,看似正确的解决方案在实现细节上存在缺陷。

1
如果 n 超出范围 [-999999999999997, 999999999999997],它将无法工作。 - PADYMKO
@SetiVolkylany,我已经在Python2.7和3.5上测试了50位数字。只需执行assert list(range(1,51)) == [math.floor(math.log10(n))+1 for n in (10**e for e in range(50))]即可。 - BiGYaN
2
尝试使用Python2.7或Python3.5:15.0 >>> math.floor(math.log10(999999999999998))+1 16.0``` 请查看我的答案https://dev59.com/UHI95IYBdhLWcg3wtwRe#42736085。 - PADYMKO

14

好的,如果不转换为字符串,我会这样做:

def lenDigits(x): 
    """
    Assumes int(x)
    """

    x = abs(x)

    if x < 10:
        return 1

    return 1 + lenDigits(x / 10)

极简递归胜利


2
您将会到达大数的递归限制。 - nog642

12

在不将整数转换为字符串的情况下计算数字的数量:

x=123
x=abs(x)
i = 0
while x >= 10**i:
    i +=1
# i is the number of digits

很好,它完全避免了字符串转换。 - Patrick Mutuku

12

如用户@Calvintwr所述,函数math.log10在范围外的数字[-999999999999997, 999999999999997]中存在浮点数误差问题。我在JavaScript(Google V8和NodeJS)和C(GNU GCC编译器)中遇到了这个问题,因此“纯数学”解决方案在这里是不可能的。


根据这个代码片段答案,亲爱的用户@Calvintwr

import math


def get_count_digits(number: int):
    """Return number of digits in a number."""

    if number == 0:
        return 1

    number = abs(number)

    if number <= 999999999999997:
        return math.floor(math.log10(number)) + 1

    count = 0
    while number:
        count += 1
        number //= 10
    return count

我在长度最长为20(含)的数字上进行了测试,一切正常。这应该足够了,因为在64位系统上,整数的最大长度是19(len(str(sys.maxsize)) == 19)。

assert get_count_digits(-99999999999999999999) == 20
assert get_count_digits(-10000000000000000000) == 20
assert get_count_digits(-9999999999999999999) == 19
assert get_count_digits(-1000000000000000000) == 19
assert get_count_digits(-999999999999999999) == 18
assert get_count_digits(-100000000000000000) == 18
assert get_count_digits(-99999999999999999) == 17
assert get_count_digits(-10000000000000000) == 17
assert get_count_digits(-9999999999999999) == 16
assert get_count_digits(-1000000000000000) == 16
assert get_count_digits(-999999999999999) == 15
assert get_count_digits(-100000000000000) == 15
assert get_count_digits(-99999999999999) == 14
assert get_count_digits(-10000000000000) == 14
assert get_count_digits(-9999999999999) == 13
assert get_count_digits(-1000000000000) == 13
assert get_count_digits(-999999999999) == 12
assert get_count_digits(-100000000000) == 12
assert get_count_digits(-99999999999) == 11
assert get_count_digits(-10000000000) == 11
assert get_count_digits(-9999999999) == 10
assert get_count_digits(-1000000000) == 10
assert get_count_digits(-999999999) == 9
assert get_count_digits(-100000000) == 9
assert get_count_digits(-99999999) == 8
assert get_count_digits(-10000000) == 8
assert get_count_digits(-9999999) == 7
assert get_count_digits(-1000000) == 7
assert get_count_digits(-999999) == 6
assert get_count_digits(-100000) == 6
assert get_count_digits(-99999) == 5
assert get_count_digits(-10000) == 5
assert get_count_digits(-9999) == 4
assert get_count_digits(-1000) == 4
assert get_count_digits(-999) == 3
assert get_count_digits(-100) == 3
assert get_count_digits(-99) == 2
assert get_count_digits(-10) == 2
assert get_count_digits(-9) == 1
assert get_count_digits(-1) == 1
assert get_count_digits(0) == 1
assert get_count_digits(1) == 1
assert get_count_digits(9) == 1
assert get_count_digits(10) == 2
assert get_count_digits(99) == 2
assert get_count_digits(100) == 3
assert get_count_digits(999) == 3
assert get_count_digits(1000) == 4
assert get_count_digits(9999) == 4
assert get_count_digits(10000) == 5
assert get_count_digits(99999) == 5
assert get_count_digits(100000) == 6
assert get_count_digits(999999) == 6
assert get_count_digits(1000000) == 7
assert get_count_digits(9999999) == 7
assert get_count_digits(10000000) == 8
assert get_count_digits(99999999) == 8
assert get_count_digits(100000000) == 9
assert get_count_digits(999999999) == 9
assert get_count_digits(1000000000) == 10
assert get_count_digits(9999999999) == 10
assert get_count_digits(10000000000) == 11
assert get_count_digits(99999999999) == 11
assert get_count_digits(100000000000) == 12
assert get_count_digits(999999999999) == 12
assert get_count_digits(1000000000000) == 13
assert get_count_digits(9999999999999) == 13
assert get_count_digits(10000000000000) == 14
assert get_count_digits(99999999999999) == 14
assert get_count_digits(100000000000000) == 15
assert get_count_digits(999999999999999) == 15
assert get_count_digits(1000000000000000) == 16
assert get_count_digits(9999999999999999) == 16
assert get_count_digits(10000000000000000) == 17
assert get_count_digits(99999999999999999) == 17
assert get_count_digits(100000000000000000) == 18
assert get_count_digits(999999999999999999) == 18
assert get_count_digits(1000000000000000000) == 19
assert get_count_digits(9999999999999999999) == 19
assert get_count_digits(10000000000000000000) == 20
assert get_count_digits(99999999999999999999) == 20

所有代码示例均在Python 3.5下测试过


5

这是一个臃肿但快速的版本:

def nbdigit ( x ):
    if x >= 10000000000000000 : # 17 -
        return len( str( x ))
    if x < 100000000 : # 1 - 8
        if x < 10000 : # 1 - 4
            if x < 100             : return (x >= 10)+1 
            else                   : return (x >= 1000)+3
        else: # 5 - 8                                                 
            if x < 1000000         : return (x >= 100000)+5 
            else                   : return (x >= 10000000)+7
    else: # 9 - 16 
        if x < 1000000000000 : # 9 - 12
            if x < 10000000000     : return (x >= 1000000000)+9 
            else                   : return (x >= 100000000000)+11
        else: # 13 - 16
            if x < 100000000000000 : return (x >= 10000000000000)+13 
            else                   : return (x >= 1000000000000000)+15

对于不太大的数字,仅需5次比较。在我的计算机上,它比math.log10版本快约30%,比len(str())版本快5%。如果你没有疯狂使用它,那么就不如此吸引人。

这是我用来测试/测量函数的一组数字:

n = [ int( (i+1)**( 17/7. )) for i in xrange( 1000000 )] + [0,10**16-1,10**16,10**16+1]

注意:它不处理负数,但适应很容易...

我们真是太疯狂了,总是追求最快速的智能解决方案。其实并没有那么多条件检查,它能极大地改善一切,而且并不难看,但这并不是最受欢迎的解决方案。如果我们担心数字精度,一开始就不应该使用Python整数。而且代码量也不算多!我们应该让事情变得更简单、更容易。感谢你提醒我这一点,Captain'Flam :D - Jaacko Torus

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