你怎么能得到一个int(C#)的第一个数字?

在C#中,获取int中第一个数字的最佳方法是什么? 我想到的方法是把int变成一个string,findstring的第一个字符,然后把它变回一个int。

int start = Convert.ToInt32(curr.ToString().Substring(0, 1)); 

虽然这样做的工作,感觉就像这样一个问题可能是一个好的,简单的,基于math的解决scheme。 string操作感觉笨重。

编辑:无论速度差异,mystring [0]而不是Substring()仍然只是string操作

就是这样

 int i = Math.Abs(386792); while(i >= 10) i /= 10; 

i将包含你所需要的

基准

首先,您必须决定“最佳”解决scheme的含义,当然这需要考虑algorithm的效率,可读性/可维护性以及将来可能出现的错误。 但仔细的unit testing通常可以避免这些问题。

我运行了这些例子中的每一个1000万次,结果值是已经通过的ElapsedTicks的数量。

从最慢到最快,algorithm是:

转换为string,取第一个字符

 int firstDigit = (int)(Value.ToString()[0]) - 48; 

结果:

 12,552,893 ticks 

使用对数

 int firstDigit = (int)(Value / Math.Pow(10, (int)Math.Floor(Math.Log10(Value)))); 

结果:

 9,165,089 ticks 

循环

 while (number >= 10) number /= 10; 

结果:

 6,001,570 ticks 

条件语句

 int firstdigit; if (Value < 10) firstdigit = Value; else if (Value < 100) firstdigit = Value / 10; else if (Value < 1000) firstdigit = Value / 100; else if (Value < 10000) firstdigit = Value / 1000; else if (Value < 100000) firstdigit = Value / 10000; else if (Value < 1000000) firstdigit = Value / 100000; else if (Value < 10000000) firstdigit = Value / 1000000; else if (Value < 100000000) firstdigit = Value / 10000000; else if (Value < 1000000000) firstdigit = Value / 100000000; else firstdigit = Value / 1000000000; 

结果:

 1,421,659 ticks 

展开和优化循环

 if (i >= 100000000) i /= 100000000; if (i >= 10000) i /= 10000; if (i >= 100) i /= 100; if (i >= 10) i /= 10; 

结果:

 1,399,788 ticks 

注意:

每个testing调用Random.Next()来获取下一个int

尝试这个

 public int GetFirstDigit(int number) { if ( number < 10 ) { return number; } return GetFirstDigit ( (number - (number % 10)) / 10); } 

编辑

有几个人要求循环版本

 public static int GetFirstDigitLoop(int number) { while (number >= 10) { number = (number - (number % 10)) / 10; } return number; } 

我能想到的最好的是:

 int numberOfDigits = Convert.ToInt32(Math.Floor( Math.Log10( value ) ) ); int firstDigit = value / Math.Pow( 10, numberOfDigits ); 

不是很漂亮:)

[编辑:第一个答案真的很糟糕:)]

[编辑2:我可能会build议string操作的解决scheme,但]

[编辑3:代码格式不错:)]

安东答案的变化:

  // cut down the number of divisions (assuming i is positive & 32 bits) if (i >= 100000000) i /= 100000000; if (i >= 10000) i /= 10000; if (i >= 100) i /= 100; if (i >= 10) i /= 10; 

有和Lennaert一样的想法

 int start = number == 0 ? 0 : number / (int) Math.Pow(10,Math.Floor(Math.Log10(Math.Abs(number)))); 

这也适用于负数。

如果你认为Keltex的答案是丑陋的,那就试试这个,真的很丑,甚至更快。 它展开二进制search来确定长度。

  ... leading code along the same lines /* i<10000 */ if (i >= 100){ if (i >= 1000){ return i/1000; } else /* i<1000 */{ return i/100; } } else /* i<100*/ { if (i >= 10){ return i/10; } else /* i<10 */{ return i; } } 

PS MartinStettner也有同样的想法。

 int myNumber = 8383; char firstDigit = myNumber.ToString()[0]; // char = '8' 

一个明显而缓慢的math方法是:

 int firstDigit = (int)(i / Math.Pow(10, (int)Math.Log10(i)))); 
 int temp = i; while (temp >= 10) { temp /= 10; } 

结果在temp

我知道这不是C#,但令人惊讶的是,在Python中,“获取数字的string表示forms的第一个字符”是更快!

编辑 :不,我犯了一个错误,我忘了再次构build整数,对不起。 展开的版本是最快的。

 $ cat first_digit.py def loop(n): while n >= 10: n /= 10 return n def unrolled(n): while n >= 100000000: # yea... unlimited size int supported :) n /= 100000000 if n >= 10000: n /= 10000 if n >= 100: n /= 100 if n >= 10: n /= 10 return n def string(n): return int(str(n)[0]) $ python -mtimeit -s 'from first_digit import loop as test' \ 'for n in xrange(0, 100000000, 1000): test(n)' 10 loops, best of 3: 275 msec per loop $ python -mtimeit -s 'from first_digit import unrolled as test' \ 'for n in xrange(0, 100000000, 1000): test(n)' 10 loops, best of 3: 149 msec per loop $ python -mtimeit -s 'from first_digit import string as test' \ 'for n in xrange(0, 100000000, 1000): test(n)' 10 loops, best of 3: 284 msec per loop $ 

我只是偶然发现了这个老问题,并且觉得倾向于提出另一个build议,因为到目前为止还没有其他答案能够为所有可能的input值返回正确的结果,而且它仍然可以更快:

 public static int GetFirstDigit( int i ) { if( i < 0 && ( i = -i ) < 0 ) return 2; return ( i < 100 ) ? ( i < 1 ) ? 0 : ( i < 10 ) ? i : i / 10 : ( i < 1000000 ) ? ( i < 10000 ) ? ( i < 1000 ) ? i / 100 : i / 1000 : ( i < 100000 ) ? i / 10000 : i / 100000 : ( i < 100000000 ) ? ( i < 10000000 ) ? i / 1000000 : i / 10000000 : ( i < 1000000000 ) ? i / 100000000 : i / 1000000000; } 

这适用于所有有符号的整数值,包括-2147483648这是最小的有符号整数,并没有一个正的对应。 Math.Abs( -2147483648 )会触发System.OverflowException- -2147483648计算为-2147483648

实现可以被看作是目前为止两个最快实现的优点的组合。 它使用二进制search,避免了多余的分割。 对循环索引进行快速的基准testing,其迭代次数为100,000,000次,表明它是当前最快实现的两倍。

它在2,829,581个蜱之后结束

为了比较,我还测量了当前最快的实施方式的一个修正的变体,其中有5,664,627个滴答。

 public static int GetFirstDigitX( int i ) { if( i < 0 && ( i = -i ) < 0 ) return 2; if( i >= 100000000 ) i /= 100000000; if( i >= 10000 ) i /= 10000; if( i >= 100 ) i /= 100; if( i >= 10 ) i /= 10; return i; } 

在我的计算机上进行这个testing的同样的更正所接受的答案需要16,561,929个滴答声。

 public static int GetFirstDigitY( int i ) { if( i < 0 && ( i = -i ) < 0 ) return 2; while( i >= 10 ) i /= 10; return i; } 

像这些简单的函数可以很容易地certificate正确性,因为在当前的硬件上迭代所有可能的整数值不会超过几秒钟。 这意味着以特别可读的方式实现它们并不那么重要,因为以后根本不需要修正一个bug。

非常简单(可能相当快,因为​​它只涉及比较和一个部门):

 if(i<10) firstdigit = i; else if (i<100) firstdigit = i/10; else if (i<1000) firstdigit = i/100; else if (i<10000) firstdigit = i/1000; else if (i<100000) firstdigit = i/10000; else (etc... all the way up to 1000000000) 

与我的同事在这里进行了一些testing,发现大部分解决scheme对0以下的数字都不起作用。

  public int GetFirstDigit(int number) { number = Math.Abs(number); <- makes sure you really get the digit! if (number < 10) { return number; } return GetFirstDigit((number - (number % 10)) / 10); } 

使用下面的所有例子来得到这个代码:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace Benfords { class Program { static int FirstDigit1(int value) { return Convert.ToInt32(value.ToString().Substring(0, 1)); } static int FirstDigit2(int value) { while (value >= 10) value /= 10; return value; } static int FirstDigit3(int value) { return (int)(value.ToString()[0]) - 48; } static int FirstDigit4(int value) { return (int)(value / Math.Pow(10, (int)Math.Floor(Math.Log10(value)))); } static int FirstDigit5(int value) { if (value < 10) return value; if (value < 100) return value / 10; if (value < 1000) return value / 100; if (value < 10000) return value / 1000; if (value < 100000) return value / 10000; if (value < 1000000) return value / 100000; if (value < 10000000) return value / 1000000; if (value < 100000000) return value / 10000000; if (value < 1000000000) return value / 100000000; return value / 1000000000; } static int FirstDigit6(int value) { if (value >= 100000000) value /= 100000000; if (value >= 10000) value /= 10000; if (value >= 100) value /= 100; if (value >= 10) value /= 10; return value; } const int mcTests = 1000000; static void Main(string[] args) { Stopwatch lswWatch = new Stopwatch(); Random lrRandom = new Random(); int liCounter; lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit1(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 1, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit2(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 2, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit3(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 3, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit4(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 4, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit5(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 5, lswWatch.ElapsedTicks); lswWatch.Reset(); lswWatch.Start(); for (liCounter = 0; liCounter < mcTests; liCounter++) FirstDigit6(lrRandom.Next()); lswWatch.Stop(); Console.WriteLine("Test {0} = {1} ticks", 6, lswWatch.ElapsedTicks); Console.ReadLine(); } } } 

我在AMD Ahtlon 64 X2 Dual Core 4200+(2.2 GHz)上获得这些结果:

 Test 1 = 2352048 ticks Test 2 = 614550 ticks Test 3 = 1354784 ticks Test 4 = 844519 ticks Test 5 = 150021 ticks Test 6 = 192303 ticks 

但得到这些AMD FX 8350八核(4.00 GHz)

 Test 1 = 3917354 ticks Test 2 = 811727 ticks Test 3 = 2187388 ticks Test 4 = 1790292 ticks Test 5 = 241150 ticks Test 6 = 227738 ticks 

所以方法5或方法6是否更快取决于CPU,我只能推测这是因为在CPU的命令处理器中的分支预测在新处理器上更聪明,但我不太确定。

我没有任何英特尔CPU,也许有人可以为我们testing它?

 while (i > 10) { i = (Int32)Math.Floor((Decimal)i / 10); } // i is now the first int 

非迭代公式:

 public static int GetHighestDigit(int num) { if (num <= 0) return 0; return (int)((double)num / Math.Pow(10f, Math.Floor(Math.Log10(num)))); } 

只是为了给你一个select,你可以重复整除10,然后回滚一个值,一旦你达到零。 由于string操作通常很慢,这可能比string操作更快,但绝不是优雅的。

像这样的东西:

 while(curr>=10) curr /= 10; 
 start = getFirstDigit(start); public int getFirstDigit(final int start){ int number = Math.abs(start); while(number > 10){ number /= 10; } return number; } 

要么

 public int getFirstDigit(final int start){ return getFirstDigit(Math.abs(start), true); } private int getFirstDigit(final int start, final boolean recurse){ if(start < 10){ return start; } return getFirstDigit(start / 10, recurse); } 
 int start = curr; while (start >= 10) start /= 10; 

这比ToString()方法更有效率,它在内部必须实现一个类似的循环,并且必须在途中构造(并parsing)一个string对象。

非常简单的方法来获得最后一位数字:

 int myInt = 1821; int lastDigit = myInt - ((myInt/10)*10); // 1821 - 1820 = 1 

这是我平时做的,请参考下面的我的function:

这个function可以从任何可以修改的string中提取出第一个数字,并根据您的使用情况使用这个函数

  public static int GetFirstNumber(this string strInsput) { int number = 0; string strNumber = ""; bool bIsContNo = true; bool bNoOccued = false; try { var arry = strInsput.ToCharArray(0, strInsput.Length - 1); foreach (char item in arry) { if (char.IsNumber(item)) { strNumber = strNumber + item.ToString(); bIsContNo = true; bNoOccued = true; } else { bIsContNo = false; } if (bNoOccued && !bIsContNo) { break; } } number = Convert.ToInt32(strNumber); } catch (Exception ex) { return 0; } return number; } 

这是一个不涉及循环的简单方法

 int number = 1234 int firstDigit = Math.Floor(number/(Math.Pow(10, number.ToString().length - 1)) 

那会给我们1234 / Math.Pow(10,4-1)= 1234/1000 = 1

 int i = 4567789; int digit1 = int.Parse(i.ToString()[0].ToString());