Fastest way to separate the digits of an int into an array in .NET?

后端 未结 11 785
天涯浪人
天涯浪人 2021-01-31 20:07

I want to separate the digits of an integer, say 12345, into an array of bytes {1,2,3,4,5}, but I want the most performance effective way to do that, because my program does tha

相关标签:
11条回答
  • 2021-01-31 20:52

    The allocation of a new int[] every time takes up a significant amount of the time according to my testing. If you know these values will be used once and thrown away before the next call, you could instead reuse a static array for a significant speed improvement:

        private static readonly int[] _buffer = new int[10];
        public static int[] ConvertToArrayOfDigits(int value)
        {
            for (int index = 9; index >= 0; index--)
            {
                _buffer[index] = value % 10;
                value = value / 10;
            }
            return _buffer;
        }
    

    to keep the code small, I am returning trailing zero's for smaller numbers, but this could easily be changed by using 9 different static arrays instead (or an array of arrays).

    Alternatively, 2 seperate ConvertToArrayOfDigits methods could be provided, one taking a precreated int array as an extra parameter, and one without that which creates the resulting buffer prior to calling the first method.

        public static void ConvertToArrayOfDigits(int value, int[] digits) { ... }
        public static int[] ConvertToArrayOfDigits(int value)
        {
            int size = DetermineDigitCount(value);
            int[] digits = new int[size];
            ConvertToArrayOfDigits(value, digits);
            return digits;
        }
    

    This way, it would be up to the caller to potentially create a static reusable buffer if their usecase allows for it.

    0 讨论(0)
  • 2021-01-31 20:56

    I haven't benchmarked this or anything, but I think this would be the simplest answer. Correct me if I'm wrong.

        Dim num As Integer = 147483647
        Dim nDigits As Integer = 1 + Convert.ToInt32(Math.Floor(Math.Log10(num)))
        Dim result(nDigits - 1) As Integer
    
        For a As Integer = 1 To nDigits
            result(a - 1) = Int(num / (10 ^ (nDigits - a))) Mod 10
        Next
    

    ** EDIT **

    Revised the function because exponents seem to be very expensive.

    Private Function Calc(ByVal num As Integer) As Integer()
        Dim nDigits As Int64 = 1 + Convert.ToInt64(Math.Floor(Math.Log10(num)))
        Dim result(nDigits - 1) As Integer
        Dim place As Integer = 1
    
        For a As Integer = 1 To nDigits
            result(nDigits - a) = Int(num / place) Mod 10
            place = place * 10
        Next
    
        Return result
    End Function
    

    This benchmarks to around 775k/sec (for numbers 9 digits or less). Drop the maximum digits to 7 and it benches at 885k/s. 5 digits at 1.1m/s.

    0 讨论(0)
  • 2021-01-31 20:59

    'Will' vs 'Does'? I'm a huge fan of optimizing code after it's wrote, profiled, and it's been determined to be the bottleneck.

    0 讨论(0)
  • 2021-01-31 20:59

    divide and mod tend to be slow operations. I wanted to find out if a solution using multiply and subtraction would be faster and it seems to be (on my computer):

        public static void ConvertToArrayOfDigits2(int value, int[] digits)
        {
            double v = value;
            double vby10 = v * .1;
    
            for (int index = digits.Length - 1; index >= 0; index--)
            {
                int ivby10 = (int)vby10;
                digits[index] = (int)(v)- ivby10* 10;
                v = ivby10;
                vby10 = ivby10 * .1;
            }       
        }
    

    I am passing in an array instead of allocating it every time to take the memory allocator and length out of the equation. This version will produce leading zeros if the array is longer than the number. Compared to a similarly converted version of Jon's example:

        public static void ConvertToArrayOfDigits(int value, int[] digits){
    
            for (int index = digits.Length - 1; index >= 0; index--)    { 
                digits[index] = value % 10;    
                value = value / 10;  
            }   
        }
    

    the no divide/mod version took about 50 more time to generate all the arrays up to a given number. I also tried using floats and it was only about 5-10% slower (the double version was quicker than the float version).

    Just because it was bothering me, here is an unrolled version which is just slightly faster again:

            public static void ConvertToArrayOfDigits3(int value, int[] digits)
        {
            double v = value;
            double vby10 = v * .1;
            int ivby10;
    
            switch(digits.Length -1){
                default:
                    throw new ArgumentOutOfRangeException();
                case 10:
                    ivby10 = (int)vby10;
                    digits[10] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 9;
                case 9:
                    ivby10 = (int)vby10;
                    digits[9] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 8;
                case 8:
                    ivby10 = (int)vby10;
                    digits[8] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 7;
                case 7:
                    ivby10 = (int)vby10;
                    digits[7] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 6;
                case 6:
                    ivby10 = (int)vby10;
                    digits[6] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 5;
                case 5:
                    ivby10 = (int)vby10;
                    digits[5] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 4;
                case 4:
                    ivby10 = (int)vby10;
                    digits[4] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 3;
                case 3:
                    ivby10 = (int)vby10;
                    digits[3] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 2;
                case 2:
                    ivby10 = (int)vby10;
                    digits[2] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 1;
                case 1:
                    ivby10 = (int)vby10;
                    digits[1] = (int)(v) - ivby10 * 10;
                    v = ivby10;
                    vby10 = ivby10 * .1;
                    goto case 0;
                case 0:
                    ivby10 = (int)vby10;
                    digits[0] = (int)(v) - ivby10 * 10;
                    break;
            }
    
        }
    
    0 讨论(0)
  • 2021-01-31 21:00

    convert integer to string and then use String.Chars[]

    0 讨论(0)
提交回复
热议问题