获取int的位数的方法?

fns*_*nst 372 java int

获取int的长度是否比这种方法更简洁?

int length = String.valueOf(1000).length();
Run Code Online (Sandbox Code Playgroud)

Mic*_*rdt 333

你的基于String的解决方案完全没问题,没有什么"不整齐"的.你必须在数学上认识到,数字没有长度,也没有数字.长度和数字都是特定基数中数字的物理表示的属性,即字符串.

基于对数的解决方案(基于字符串的)在内部执行(某些)相同的操作,并且可能更快(无关紧要)更快,因为它只生成长度并忽略数字.但我实际上并不认为它的意图更清晰 - 这是最重要的因素.

  • +1在选择解决问题的方法时考虑代码的意图 (49认同)
  • 一些(很晚)添加:对于负值可能无法正常工作,这取决于您是否期望" - "是否为数字.但是,添加`Math.abs()`会解决这个问题. (10认同)
  • Datapoint:在我的机器上,log方法的运行速度似乎是字符串长度方法的两倍.如果方法被调用很多或者在时间关键的代码段中,我不会称之为无关紧要. (4认同)
  • @CPerkins.过早优化.你知道高谈阔论. (4认同)
  • 请参阅下面的基准单元测试(这也可能有缺陷,我不是基准测试专家)。在大量运行(100 000 000)后,我的机器上的速度是 11 秒到 8 秒,几乎是其两倍。 (2认同)

Dmi*_*ant 256

对数是你的朋友:

int n = 1000;
int length = (int)(Math.log10(n)+1);
Run Code Online (Sandbox Code Playgroud)

注意:仅对n> 0有效.

  • 工作...除非你检查的值= 0,这将给你奇怪的结果(-2147483647).Math.log10 API:"如果参数为正零或负零,则结果为负无穷大." (8认同)
  • 这比使用我的变体更快还是更好? (2认同)
  • 我认为这个意图非常明确. (2认同)
  • @Tom为什么会认为它很贵?有人可能会假设数学协处理器会执行它,因此它可能接近加法的速度。即使Java现在不使用协处理器,也可以假设它可能会...(我们只是忽略了您对Java速度慢的未经教育的暗示,因为您可能对证据不感兴趣-或如果您是,请访问http://shootout.alioth.debian.org/自己找) (2认同)
  • +1提供不涉及对象内存分配的方法,这是最大化重用以避免GC集合的必要条件. (2认同)

Mar*_*ian 153

最快的方法:分而治之.

假设您的范围是0到MAX_INT,那么您有1到10位数.您可以使用除法和征服来处理此间隔,每个输入最多可进行4次比较.首先,通过一次比较将[1..10]划分为[1..5]和[6..10],然后使用一次比较将每个长度为5的间隔划分为一个长度3和一个长度2间隔.长度2间隔需要一次比较(总共3次比较),长度3间隔可以分为长度1间隔(解)和长度2间隔.所以,你需要3或4个比较.

没有划分,没有浮点运算,没有昂贵的对数,只有整数比较.

代码(长但快):

if (n < 100000){
        // 5 or less
        if (n < 100){
            // 1 or 2
            if (n < 10)
                return 1;
            else
                return 2;
        }else{
            // 3 or 4 or 5
            if (n < 1000)
                return 3;
            else{
                // 4 or 5
                if (n < 10000)
                    return 4;
                else
                    return 5;
            }
        }
    } else {
        // 6 or more
        if (n < 10000000) {
            // 6 or 7
            if (n < 1000000)
                return 6;
            else
                return 7;
        } else {
            // 8 to 10
            if (n < 100000000)
                return 8;
            else {
                // 9 or 10
                if (n < 1000000000)
                    return 9;
                else
                    return 10;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

基准测试(在JVM热身之后) - 请参阅下面的代码以了解基准测试的运行方式:

  1. 基线方法(使用String.length):2145ms
  2. log10方法:711ms =比基线快3.02倍
  3. 重复划分:2797ms =比基线快0.77倍
  4. 分而治之:74ms =
    比基线快28.99 倍

完整代码:

public static void main(String[] args)
throws Exception
{

    // validate methods:
    for (int i = 0; i < 1000; i++)
        if (method1(i) != method2(i))
            System.out.println(i);
    for (int i = 0; i < 1000; i++)
        if (method1(i) != method3(i))
            System.out.println(i + " " + method1(i) + " " + method3(i));
    for (int i = 333; i < 2000000000; i += 1000)
        if (method1(i) != method3(i))
            System.out.println(i + " " + method1(i) + " " + method3(i));
    for (int i = 0; i < 1000; i++)
        if (method1(i) != method4(i))
            System.out.println(i + " " + method1(i) + " " + method4(i));
    for (int i = 333; i < 2000000000; i += 1000)
        if (method1(i) != method4(i))
            System.out.println(i + " " + method1(i) + " " + method4(i));

    // work-up the JVM - make sure everything will be run in hot-spot mode
    allMethod1();
    allMethod2();
    allMethod3();
    allMethod4();

    // run benchmark
    Chronometer c;

    c = new Chronometer(true);
    allMethod1();
    c.stop();
    long baseline = c.getValue();
    System.out.println(c);

    c = new Chronometer(true);
    allMethod2();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline");

    c = new Chronometer(true);
    allMethod3();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline");

    c = new Chronometer(true);
    allMethod4();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline");
}


private static int method1(int n)
{
    return Integer.toString(n).length();
}
private static int method2(int n)
{
    if (n == 0)
        return 1;
    return (int)(Math.log10(n) + 1);
}
private static int method3(int n)
{
    if (n == 0)
        return 1;
    int l;
    for (l = 0 ; n > 0 ;++l)
        n /= 10;
    return l;
}
private static int method4(int n)
{
    if (n < 100000)
    {
        // 5 or less
        if (n < 100)
        {
            // 1 or 2
            if (n < 10)
                return 1;
            else
                return 2;
        }
        else
        {
            // 3 or 4 or 5
            if (n < 1000)
                return 3;
            else
            {
                // 4 or 5
                if (n < 10000)
                    return 4;
                else
                    return 5;
            }
        }
    }
    else
    {
        // 6 or more
        if (n < 10000000)
        {
            // 6 or 7
            if (n < 1000000)
                return 6;
            else
                return 7;
        }
        else
        {
            // 8 to 10
            if (n < 100000000)
                return 8;
            else
            {
                // 9 or 10
                if (n < 1000000000)
                    return 9;
                else
                    return 10;
            }
        }
    }
}


private static int allMethod1()
{
    int x = 0;
    for (int i = 0; i < 1000; i++)
        x = method1(i);
    for (int i = 1000; i < 100000; i += 10)
        x = method1(i);
    for (int i = 100000; i < 1000000; i += 100)
        x = method1(i);
    for (int i = 1000000; i < 2000000000; i += 200)
        x = method1(i);

    return x;
}
private static int allMethod2()
{
    int x = 0;
    for (int i = 0; i < 1000; i++)
        x = method2(i);
    for (int i = 1000; i < 100000; i += 10)
        x = method2(i);
    for (int i = 100000; i < 1000000; i += 100)
        x = method2(i);
    for (int i = 1000000; i < 2000000000; i += 200)
        x = method2(i);

    return x;
}
private static int allMethod3()
{
    int x = 0;
    for (int i = 0; i < 1000; i++)
        x = method3(i);
    for (int i = 1000; i < 100000; i += 10)
        x = method3(i);
    for (int i = 100000; i < 1000000; i += 100)
        x = method3(i);
    for (int i = 1000000; i < 2000000000; i += 200)
        x = method3(i);

    return x;
}
private static int allMethod4()
{
    int x = 0;
    for (int i = 0; i < 1000; i++)
        x = method4(i);
    for (int i = 1000; i < 100000; i += 10)
        x = method4(i);
    for (int i = 100000; i < 1000000; i += 100)
        x = method4(i);
    for (int i = 1000000; i < 2000000000; i += 200)
        x = method4(i);

    return x;
}
Run Code Online (Sandbox Code Playgroud)

基准:

  1. 基线方法(使用String.length):2145ms
  2. log10方法:711ms =比基线快3.02倍
  3. 重复划分:2797ms =比基线快0.77倍
  4. 分而治之:74ms =
    比基线快28.99 倍

编辑: 在我编写基准测试之后,我从Java 6中获得了Integer.toString的潜行峰值,我发现它使用:

final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                  99999999, 999999999, Integer.MAX_VALUE };

// Requires positive x
static int stringSize(int x) {
    for (int i=0; ; i++)
        if (x <= sizeTable[i])
            return i+1;
}
Run Code Online (Sandbox Code Playgroud)

我将它与我的分而治之的解决方案进行了对比:

  1. 分而治之:104ms
  2. Java 6解决方案 - 迭代并比较:406ms

我的速度提高了约4倍.

  • 谈论过早优化:D (82认同)
  • 使用三元运算符,将其降至101个字符:`n <100000?n <100?n <10?1:2:n <1000?3:n <10000?4:5:n <10000000?n <1000000 ?6:7:N <亿8:N <十亿9:10` (14认同)
  • 这看起来很棒.您可以使用?:运算符将其写得更紧凑,以获得更多的接受度 (6认同)
  • 我喜欢!如何切换块而不是嵌套的if-elseses? (2认同)
  • 我没有意识到所有这些 if else 语句会比将 int 转换为 String 然后调用 .length 快得多。+1 (2认同)

Jay*_*Jay 13

关于您的基准测试的两条评论:Java是一个复杂的环境,即时编译和垃圾收集等等,所以为了得到公平的比较,每当我运行基准测试时,我总是:(a)附上两个测试在循环中按顺序运行它们5或10次.第二次通过循环的运行时通常与第一次完全不同.并且(b)在每个"方法"之后,我做一个System.gc()来尝试触发垃圾收集.否则,第一种方法可能会生成一堆对象,但不足以强制进行垃圾收集,然后第二种方法会创建一些对象,堆耗尽,垃圾收集运行.然后第二种方法被"收费"用于拾取第一种方法留下的垃圾.非常不公平!

也就是说,上述两个都没有在这个例子中产生显着差异.

无论有没有这些修改,我得到的结果都与你的不同.当我运行它时,是的,toString方法的运行时间为6400到6600毫秒,而日志方法则超过20,000到20,400毫秒.对于我来说,日志方法速度慢了3倍,而不是稍微快一些.

请注意,这两种方法涉及非常不同的成本,因此这并不是完全令人震惊:toString方法将创建许多必须清理的临时对象,而日志方法需要更加强烈的计算.所以可能不同的是,在内存较少的机器上,toString需要更多的垃圾收集轮次,而在处理器速度较慢的机器上,额外的日志计算会更加痛苦.

我也尝试了第三种方法.我写了这个小函数:

static int numlength(int n)
{
    if (n == 0) return 1;
    int l;
    n=Math.abs(n);
    for (l=0;n>0;++l)
        n/=10;
    return l;           
}
Run Code Online (Sandbox Code Playgroud)

它运行在1600到1900毫秒之间 - 不到toString方法的1/3,并且是我机器上日志方法的1/10.

如果您有多种数字,可以通过开始除以1,000或1,000,000来进一步加快速度,以减少循环次数.我没玩过那个.


San*_*osh 11

使用Java

int nDigits = Math.floor(Math.log10(Math.abs(the_integer))) + 1;
Run Code Online (Sandbox Code Playgroud)

使用import java.lang.Math.*;在开始

使用C.

int nDigits = floor(log10(abs(the_integer))) + 1;
Run Code Online (Sandbox Code Playgroud)

使用inclue math.h在开始

  • 仅供参考,如果`the_integer` 为`0` 将导致无穷大,因此请检查它。 (2认同)

moo*_*ful 9

还不能发表评论,所以我将作为一个单独的答案发布.

基于对数的解决方案不计算非常大的长整数的正确位数,例如:

long n = 99999999999999999L;

// correct answer: 17
int numberOfDigits = String.valueOf(n).length();

// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1); 
Run Code Online (Sandbox Code Playgroud)

基于对数的解决方案计算大整数中的错误位数


Dir*_*irk 8

由于整数的基数10中的位数仅为1 + truncate(log10(数字)),因此您可以:

public class Test {

    public static void main(String[] args) {

        final int number = 1234;
        final int digits = 1 + (int)Math.floor(Math.log10(number));

        System.out.println(digits);
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑,因为我的上一次编辑修复了代码示例,但没有修改说明.

  • `Math.floor`有点多余,不是吗?无论如何,转换为"int"会将其向下舍入. (5认同)

Thi*_*ark 6

另一种字符串方法。简短而甜蜜 - 适用于任何整数n

int length = ("" + n).length();
Run Code Online (Sandbox Code Playgroud)


J.A*_*.L. 5

Marian的解决方案适用于型号码(最多9,223,372,036,854,775,807),以防有人想要复制和粘贴它.在我编写的程序中,对于数量高达10000的可能性更大,所以我为它们制作了一个特定的分支.无论如何,它不会产生显着的差异.

public static int numberOfDigits (long n) {     
    // Guessing 4 digit numbers will be more probable.
    // They are set in the first branch.
    if (n < 10000L) { // from 1 to 4
        if (n < 100L) { // 1 or 2
            if (n < 10L) {
                return 1;
            } else {
                return 2;
            }
        } else { // 3 or 4
            if (n < 1000L) {
                return 3;
            } else {
                return 4;
            }
        }           
    } else  { // from 5 a 20 (albeit longs can't have more than 18 or 19)
        if (n < 1000000000000L) { // from 5 to 12
            if (n < 100000000L) { // from 5 to 8
                if (n < 1000000L) { // 5 or 6
                    if (n < 100000L) {
                        return 5;
                    } else {
                        return 6;
                    }
                } else { // 7 u 8
                    if (n < 10000000L) {
                        return 7;
                    } else {
                        return 8;
                    }
                }
            } else { // from 9 to 12
                if (n < 10000000000L) { // 9 or 10
                    if (n < 1000000000L) {
                        return 9;
                    } else {
                        return 10;
                    }
                } else { // 11 or 12
                    if (n < 100000000000L) {
                        return 11;
                    } else {
                        return 12;
                    }
                }
            }
        } else { // from 13 to ... (18 or 20)
            if (n < 10000000000000000L) { // from 13 to 16
                if (n < 100000000000000L) { // 13 or 14
                    if (n < 10000000000000L) { 
                        return 13;
                    } else {
                        return 14;
                    }
                } else { // 15 or 16
                    if (n < 1000000000000000L) {
                        return 15;
                    } else {
                        return 16;
                    }
                }
            } else { // from 17 to ...¿20?
                if (n < 1000000000000000000L) { // 17 or 18
                    if (n < 100000000000000000L) {
                        return 17;
                    } else {
                        return 18;
                    }
                } else { // 19? Can it be?
                    // 10000000000000000000L is'nt a valid long.
                    return 19;
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)