获取整数的数字位数的方法?

463

有没有比这个方法更简洁的获取一个整数位数的方式?

int numDigits = String.valueOf(1000).length();

9
请定义整数的长度。 - Tom
40
我认为他想要数数字中的位数。 - Alberto Zaccagni
3
人们给你的答案是正确的,他们给出的是int的长度而不将其转换为字符串,但是为什么你不想将其转换为字符串呢?如果是速度问题,我不确定这些方法会更快。你可能需要进行一些测试(或者决定它是否真的很重要)。 - Beska
3
@ptomli 中的十六进制数字仍然是数字,只是处于不同的进位系统中。 - Mark Pim
3
@Ptomli 当然可以,但在 Integer.toString 函数和一般交谈中,十进制是默认选项。当银行告诉我:“请在此框中写入支票金额”时,我不会问他们是否应该用十进制、十六进制或八进制书写。我们默认使用十进制,除非另有规定或上下文需要。 - Jay
显示剩余4条评论
29个回答

4

那就来点老套的数学吧!将数字除以10,直到结果为0。

public static int getSize(long number) {
        int count = 0;
        while (number > 0) {
            count += 1;
            number = (number / 10);
        }
        return count;
    }

1
你测试过它了吗?你知道,即使从人类的角度来看,它是有意义的,但在机器的“思维方式”中并不真正起作用,对吧?让我提出一个建议:创建一个由两百万个数字组成的数组,最好是 Long.MAX_VALUE,这是你的代码的最坏复杂度情况,并使用 System.nanoTime() 对其他解决方案的最坏复杂度情况进行计时试验。++实际上,也尝试使用随机化器填充的数组,范围设置为 0Long.MAX_VALUE,以进行“平均复杂度”测试++ 你可能会发现结果...非常令人震惊。 - CosmicGiant
@thelima 这对于零或负数不起作用,但这只是一个小bug。在我看来,原则是正确的。你所提到的“令人震惊”的结果是什么? - Jay
让我们这样说,计算机...嗯...它们不喜欢除法。在需要处理大量大数字的情况下,每个处理数字中的每个数字都需要除法...嗯...事情会“变得非常缓慢非常快”...如果你明白我的意思... 这就是为什么您会看到许多答案在此处使用基于测试和比较的代码,使用“if”而不是除法来处理每个十进制数字:如果它不更快,至少它保持了大部分速度,无论最坏情况如何。 在大数字上使用除法和对数之间进行测试... - CosmicGiant
1
@TheLima 你在说什么?对于一个 int,这个循环最多执行11次。你有什么证据支持你的说法吗? - user207421
从硬件角度来看,除法是一个迭代的过程。我所知道最快的除法算法是基数4(radix4),每次迭代生成4位;因此32位的除法至少需要8次迭代。例如,乘法可以并行计算,并且还可以分解为更简单的乘法;从位级别分解(仅需要5个操作)或部分分解再加上查找表(传统的大小 VS 速度权衡)。问题不仅在于“有多少次迭代”,而且在于“每次迭代在硬件层面上意味着/做了什么”。 - CosmicGiant

3

我可以试试吗? ;)

基于Dirk的解决方案

final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));

请注意,Math.abs() 无法处理 Integer.MIN_VALUE - nmatt

3

现在的Marian解决方案采用三进制:

 public int len(int n){
        return (n<100000)?((n<100)?((n<10)?1:2):(n<1000)?3:((n<10000)?4:5)):((n<10000000)?((n<1000000)?6:7):((n<100000000)?8:((n<1000000000)?9:10)));
    }

因为我们能够做到。

3
这有点难读。也许可以加一些空格和/或换行符。 - michaelb958--GoFundMonica
但是它真的很便携! - Trevor Rudolph

3
没有String API,没有utils,没有类型转换,只有纯粹的Java迭代。
public static int getNumberOfDigits(int input) {
    int numOfDigits = 1;
    int base = 1;
    while (input >= base * 10) {
        base = base * 10;
        numOfDigits++;
    }
    return numOfDigits;
 }

你可以选择看涨以获取更高的价值。

2
这将无限循环所有大于1874919423的值。要了解原因,请尝试以下内容:for (int x = 1, i = 0; i < 32; ++i) { System.out.println(x *= 10); } - nmatt

1

好奇心驱使我尝试对其进行基准测试...

import org.junit.Test;
import static org.junit.Assert.*;


public class TestStack1306727 {

    @Test
    public void bench(){
        int number=1000;
        int a= String.valueOf(number).length();
        int b= 1 + (int)Math.floor(Math.log10(number));

        assertEquals(a,b);
        int i=0;
        int s=0;
        long startTime = System.currentTimeMillis();
        for(i=0, s=0; i< 100000000; i++){
            a= String.valueOf(number).length();
            s+=a;
        }
        long stopTime = System.currentTimeMillis();
        long runTime = stopTime - startTime;
        System.out.println("Run time 1: " + runTime);
        System.out.println("s: "+s);
        startTime = System.currentTimeMillis();
        for(i=0,s=0; i< 100000000; i++){
            b= number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
            s+=b;
        }
        stopTime = System.currentTimeMillis();
        runTime = stopTime - startTime;
        System.out.println("Run time 2: " + runTime);
        System.out.println("s: "+s);
        assertEquals(a,b);


    }
}

结果如下:

运行时间 1: 6765
s: 400000000
运行时间 2: 6000
s: 400000000

现在我不禁要想我的基准测试是否有意义,但我确实在多次运行基准测试时得到了一致的结果(在毫秒级别内存在变化)... :) 看起来优化这个测试是无用的...


编辑:根据ptomli的评论,我在上面的代码中将“number”替换为“i”,并在5次测试中得到以下结果:

运行时间1:11500
s:788888890
运行时间2:8547
s:788888890
运行时间1:11485 s:788888890 运行时间2:8547 s:788888890
运行时间1:11469 s:788888890 运行时间2:8547 s:788888890
运行时间1:11500 s:788888890 运行时间2:8547 s:788888890
运行时间1:11484 s:788888890 运行时间2:8547 s:788888890

1
纯粹出于乐趣,如果从0到1万亿的数字分布中看,这其中有什么差异呢? :) - ptomli

1

使用设计(基于问题)。这是分治算法的替代方法。我们将首先定义一个枚举类型(考虑它仅用于无符号整数)。

public enum IntegerLength {
    One((byte)1,10),
    Two((byte)2,100),
    Three((byte)3,1000),
    Four((byte)4,10000),
    Five((byte)5,100000),
    Six((byte)6,1000000),
    Seven((byte)7,10000000),
    Eight((byte)8,100000000),
    Nine((byte)9,1000000000);

    byte length;
    int value;

    IntegerLength(byte len,int value) {
        this.length = len;
        this.value = value;
    }

    public byte getLenght() {
        return length;
    }

    public int getValue() {
        return value;
    }
}

现在我们将定义一个类,遍历枚举值并比较返回适当的长度。
public class IntegerLenght {
    public static byte calculateIntLenght(int num) {    
        for(IntegerLength v : IntegerLength.values()) {
            if(num < v.getValue()){
                return v.getLenght();
            }
        }
        return 0;
    }
}

这个解决方案的运行时间与分治方法相同。

分而治之算法会从中间开始,将剩余的搜索区域一分为二。这个算法具有线性运行时间。但是对于仅需进行9次比较的情况来说,这并不重要。但是如果“num>=Nine.getValue()”,这个算法还能正常工作吗? - Teepeemm

0

或者,您可以检查数字是否大于或小于所需数字,而不是长度。

    public void createCard(int cardNumber, int cardStatus, int customerId) throws SQLException {
    if(cardDao.checkIfCardExists(cardNumber) == false) {
        if(cardDao.createCard(cardNumber, cardStatus, customerId) == true) {
            System.out.println("Card created successfully");
        } else {

        }
    } else {
        System.out.println("Card already exists, try with another Card Number");
        do {
            System.out.println("Enter your new Card Number: ");
            scan = new Scanner(System.in);
            int inputCardNumber = scan.nextInt();
            cardNumber = inputCardNumber;
        } while(cardNumber < 95000000);
        cardDao.createCard(cardNumber, cardStatus, customerId);
    }
}

}


我不理解,你似乎在回答不同的问题。 - Teepeemm

0

我还没有看到基于乘法的解决方案。对于数百万个测试用例,对数、除法和基于字符串的解决方案将变得相当笨重,因此这里有一个针对 ints 的解决方案:

/**
 * Returns the number of digits needed to represents an {@code int} value in 
 * the given radix, disregarding any sign.
 */
public static int len(int n, int radix) {
    radixCheck(radix); 
    // if you want to establish some limitation other than radix > 2
    n = Math.abs(n);

    int len = 1;
    long min = radix - 1;

    while (n > min) {
        n -= min;
        min *= radix;
        len++;
    }

    return len;
}

在十进制中,这是有效的,因为n本质上被比较为9、99、999...,而min则为9、90、900...,并且n被减去9、90、900...

不幸的是,由于溢出,仅仅替换每个int实例就无法将其移植到long。另一方面,碰巧它对于2和10进制是有效的(但对于大多数其他进制则失败得很惨)。您需要一个查找表来查找溢出点(或者进行除法测试...呕)。

/**
 * For radices 2 &le r &le Character.MAX_VALUE (36)
 */
private static long[] overflowpt = {-1, -1, 4611686018427387904L,
    8105110306037952534L, 3458764513820540928L, 5960464477539062500L,
    3948651115268014080L, 3351275184499704042L, 8070450532247928832L,
    1200757082375992968L, 9000000000000000000L, 5054470284992937710L,
    2033726847845400576L, 7984999310198158092L, 2022385242251558912L,
    6130514465332031250L, 1080863910568919040L, 2694045224950414864L,
    6371827248895377408L, 756953702320627062L, 1556480000000000000L,
    3089447554782389220L, 5939011215544737792L, 482121737504447062L,
    839967991029301248L, 1430511474609375000L, 2385723916542054400L,
    3902460517721977146L, 6269893157408735232L, 341614273439763212L,
    513726300000000000L, 762254306892144930L, 1116892707587883008L,
    1617347408439258144L, 2316231840055068672L, 3282671350683593750L,
    4606759634479349760L};

public static int len(long n, int radix) {
    radixCheck(radix);
    n = abs(n);

    int len = 1;
    long min = radix - 1;
    while (n > min) {
        len++;
        if (min == overflowpt[radix]) break;
        n -= min;
        min *= radix;

    }

    return len;
}

0
我们可以使用递归循环来实现这个。
    public static int digitCount(int numberInput, int i) {
        while (numberInput > 0) {
        i++;
        numberInput = numberInput / 10;
        digitCount(numberInput, i);
        }
        return i;
    }

    public static void printString() {
        int numberInput = 1234567;
        int digitCount = digitCount(numberInput, 0);

        System.out.println("Count of digit in ["+numberInput+"] is ["+digitCount+"]");
    }

0

这取决于你对“整洁”的理解。我认为以下代码非常整洁,而且运行速度快。

它基于Marian答案,扩展到适用于所有long值,并使用? :操作符进行渲染。

private static long[] DIGITS = { 1l,
                                 10l,
                                 100l,
                                 1000l,
                                 10000l,
                                 100000l,
                                 1000000l,
                                 10000000l,
                                 100000000l,
                                 1000000000l,
                                 10000000000l,
                                 100000000000l,
                                 1000000000000l,
                                 10000000000000l,
                                 100000000000000l,
                                 1000000000000000l,
                                 10000000000000000l,
                                 100000000000000000l,
                                 1000000000000000000l };

public static int numberOfDigits(final long n)
{
    return n == Long.MIN_VALUE ? 19 : n < 0l ? numberOfDigits(-n) :
            n < DIGITS[8] ? // 1-8
              n < DIGITS[4] ? // 1-4
                n < DIGITS[2] ? // 1-2
                  n < DIGITS[1] ? 1 : 2 : // 1-2
                        n < DIGITS[3] ? 3 : 4 : // 3-4
                      n < DIGITS[6] ? // 5-8
                        n < DIGITS[5] ? 5 : 6 : // 5-6
                      n < DIGITS[7] ? 7 : 8 : // 7-8
            n < DIGITS[16] ? // 9-16
              n < DIGITS[12] ? // 9-12
                n < DIGITS[10] ? // 9-10
                  n < DIGITS[9] ? 9 : 10 : // 9-10
                        n < DIGITS[11] ? 11 : 12 : // 11-12
                      n < DIGITS[14] ? // 13-16
                        n < DIGITS[13] ? 13 : 14 : // 13-14
                      n < DIGITS[15] ? 15 : 16 : // 15-16
            n < DIGITS[17] ? 17 :  // 17-19
            n < DIGITS[18] ? 18 :
            19;
}

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