有没有比这个方法更简洁的获取一个整数位数的方式?
int numDigits = String.valueOf(1000).length();
有没有比这个方法更简洁的获取一个整数位数的方式?
int numDigits = String.valueOf(1000).length();
你基于字符串的解决方案完全可行,没有任何“不整洁”的地方。你必须意识到,在数学上,数字没有长度,也没有数字。长度和数字都是特定进制下数字的 物理表示 的属性,即一个字符串。
基于对数的解决方案在内部执行与基于字符串的解决方案相同的任务(一些),并且可能会稍微快一些,因为它仅生成长度并忽略数字。但我认为它实际上并不清晰 - 这是最重要的因素。
Math.abs()
将修复这个问题。 - YingYang对数是你的朋友:
int n = 1000;
int length = (int)(Math.log10(n)+1);
注意:此条件仅适用于n > 0。
if (n < 100000) { // 1 to 5
if (n < 100) { // 1 or 2
if (n < 10) return 1;
return 2;
}
else { // 3, 4 or 5
if (n < 1000) return 3;
if (n < 10000) return 4;
return 5;
}
}
else { // 6 to 7
if (n < 10000000) { // 6 or 7
if (n < 1000000) return 6;
return 7;
}
else { // 8, 9 or 10
if (n < 100000000) return 8;
if (n < 1000000000) return 9;
return 10;
}
}
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 as fast as baseline");
c = new Chronometer(true);
allMethod3();
c.stop();
System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times as fast as baseline");
c = new Chronometer(true);
allMethod4();
c.stop();
System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times as fast as 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;
}
在我写完基准测试之后,我偷偷瞄了一眼 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;
}
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
- jgawrych对于你的基准测试,我有两点评论:Java是一个复杂的环境,涉及即时编译和垃圾回收等问题,为了公平比较,每当我运行基准测试时,我总是:(a)将两个测试用例封装在一个循环中,按顺序运行5或10次。经常情况下,第二遍循环的运行时间与第一遍循环的运行时间截然不同。(b) 在每个“方法”之后,我使用System.gc()尝试触发垃圾回收。否则,第一个方法可能会生成一堆对象,但不足以强制进行垃圾回收,然后第二个方法创建了一些对象,堆被耗尽,垃圾回收开始运行。接着,第二种方法就要“负责”清理第一种方法留下的垃圾,这非常不公平!
也就是说,在这个例子中,上述两点都没有作出重大的差异。
无论是加入还是不加入那些修改,我得到的结果与你的结果非常不同。当我运行时,toString方法给出的运行时间为6400到6600毫秒,而日志方法则需要20000到20400毫秒。日志方法并没有比toString方法稍微快一点,而是慢了三倍。
请注意,这两种方法涉及非常不同的成本,所以这并不完全令人震惊: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;
}
在我的计算机上,该方法的运行时间为1600到1900毫秒,少于toString方法的三分之一,以及log方法的十分之一。
如果你有一个广泛的数字范围,你可以通过从1,000或1,000,000开始除法来减少循环次数,从而进一步提高速度。我没有尝试过这种方法。
Math.abs()
对于 Integer.MIN_VALUE
不起作用,在这种情况下该方法将返回0。 - nmatt我目前无法留下评论,因此我会发布一个单独的答案。
基于对数的解决方案无法正确计算非常大的长整数的位数,例如:
long n = 99999999999999999L;
// correct answer: 17
int numberOfDigits = String.valueOf(n).length();
// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1);
使用Java
int nDigits = Math.floor(Math.log10(Math.abs(the_integer))) + 1;
在开头使用 import java.lang.Math.*;
使用C语言
int nDigits = floor(log10(abs(the_integer))) + 1;
在开头使用include math.h
Math.abs()
不适用于 Integer.MIN_VALUE
。 - nmatt由于一个整数在十进制下的位数只是1 + 截断(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);
}
}
编辑: 上次编辑只是修改了代码示例,但未修改描述。
Math.floor
有点多余,因为转换为 int
会自动向下取整。 - CompuChipMath.abs()
不能处理Integer.MIN_VALUE
。 - nmatt另一种字符串方法。简短而甜美-适用于任何整数n
。
int length = ("" + n).length();
n
和零。可以使用("" + Math.abs(n))。length()
来获取负整数的长度。 - ThisClarkMath.abs()
不适用于 Integer.MIN_VALUE
。 - nmatt("" + n).replace("-", "").length(); // prevent negatives with replace
- ThisClark为了适应长达9,223,372,036,854,775,807的数字,Marian的解决方案进行了调整,以防有人想要复制并粘贴它。在我编写此程序时,数字最多只有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;
}
}
}
}
}
我看到一些人使用字符串库甚至使用Integer类。这并没有什么问题,但是获取数字位数的算法并不复杂。在此示例中,我使用了long类型,但使用int类型同样也能很好地工作。
private static int getLength(long num) {
int count = 1;
while (num >= 10) {
num = num / 10;
count++;
}
return count;
}