1.11M Members

Faster conversions than Convert.ToDouble(), Convert.ToInt32()

 
0
 

Hello,

I wonder if there could be faster way to convert a string to .Double() and Int32(),
then the methods below?

The code below takes approx: 7000 ms

                //7000 ms
                String number1 = "21.58";
                String number2 = "21";
                double getNum1 = 0;
                int getNum2 = 0;

                DateTime dt = DateTime.Now;
                int runs = 85000 * 15 * 10;
                for (int s = 0; s < runs; s++)
                {
                    getNum1 = Convert.ToDouble(number1);
                    getNum2 = Convert.ToInt32(number2);
                }
                DateTime dt2 = DateTime.Now;
                TimeSpan diff = dt2 - dt;

                MessageBox.Show(diff.TotalMilliseconds.ToString());
 
0
 

The only method I could find was to cheat and use the VB.net 'Val(string) as double' function. The only caveat is it returns a double so I had to change getnum2 to a double. The good news, approximately 50% increase in speed. Not sure why that code is that much more efficient, but there you have it. Here's my code:

 using Microsoft.VisualBasic;
            String number1 = "21.58";
            String number2 = "21";
            double getNum1 = 0;
            double getNum2 = 0;
            DateTime dt = DateTime.Now;
            int runs = 85000 * 15 * 10;
            for (int s = 0; s < runs; s++)
            {

                getNum1 = Microsoft.VisualBasic.Conversion.Val(number1);
                getNum2 = Microsoft.VisualBasic.Conversion.Val(number2);

            }
            DateTime dt2 = DateTime.Now;
            TimeSpan diff = dt2 - dt;
            MessageBox.Show(diff.TotalMilliseconds.ToString());

I also had to add a reference to the Microsoft.VisualBasic dll.

 
0
 

I don't think 7 seconds is outrageous for almost 13 million runs of that loop. Do you have a reason for increasing the speed or is it just premature optimization?

 
0
 

Have you tested the Convert.ToDouble function against the Double.TryParse function? I'm not sure if it's faster, but it is generally the preferred approach as it handles exceptions for you.

EDIT: There is also a TryParse static function for each of the other "primitive" types (Int32, Decimal, Byte etc).

 
0
 

when I tried the parse function there was no appreciable difference.

 
0
 

Perhaps write your own conversion functions and see if they are better?

 
0
 

The trouble with writing your own subroutines, is that code bloat slows it down. It's very hard to write code that ends up more efficient than the built-in codes when you're doing conversions. You'd probably have to resort to assembly code, in order to get your code as tight as possible, which is a pretty high learning curve.

 
0
 

It's very hard to write code that ends up more efficient than the built-in codes when you're doing conversions.

Actually, it's pretty easy unless you have the same goal of being a general conversion. If you can make assumptions about either the source or destination value you can optimize by avoiding work that the more general algorithm has to do. Often that's exactly what happens when replacing standard library code with ad hoc code for performance reasons.

 
0
 

Still a pretty high learning curve for an experiment. The code so far is under 4 seconds for over 1 million iterations with 2 conversions in each iteration. I can't see any other code being cost effective in terms of time spent for insignificant or even no gain.

 
0
 

Still a pretty high learning curve for an experiment.

Assuming you'd need to learn how to do the conversion. ;)

I can't see any other code being cost effective in terms of time spent for insignificant or even no gain.

I agree completely. While I can't confidently say one way or another without confirmation from the OP, this whole thread strikes me as excessive focus on performance where there's no real benefit.

 
0
 

Have you tested the Convert.ToDouble function against the Double.TryParse function? I'm not sure if it's faster, but it is generally the preferred approach as it handles exceptions for you.

That is true, I have tested this against the Convert.ToDouble and they perform approx the same.

Yes, I am searching for more or less the best possible function to convert from string .DoDouble and .ToInt32.
My code will litteraly convert billions and billions of those numbers and I beleive, there
is some speed to gain if finding something.

However, I have find this function which is 100% faster then .ToDouble. But this code converts string to decimal.

I wonder if this code could be changed so it could work with .ToDouble and .ToInt32 ?

        static decimal CustomParseDecimal(string input)
        {
            long n = 0;
            int decimalPosition = input.Length;
            for (int k = 0; k < input.Length; k++)
            {
                char c = input[k];
                if (c == '.')
                    decimalPosition = k + 1;
                else
                    n = (n * 10) + (int)(c - '0');
            }
            return new decimal((int)n, (int)(n >> 32), 0, false, (byte)(input.Length - decimalPosition));
        }
 
0
 

Well I stand corrected. This code is a lot faster. Made the mods for double and int:

            String number1 = "21.58";
            String number2 = "21";
            double getNum1 = 0;
            Int32 getNum2 = 0;
            DateTime dt = DateTime.Now;
            int runs = 85000 * 15 * 10;
            for (int s = 0; s < runs; s++)
            {
                getNum1 = (double)(CustomParseDecimal(number1));
                getNum2 = CustomParseInt(number2);
            }
            DateTime dt2 = DateTime.Now;
            TimeSpan diff = dt2 - dt;
            MessageBox.Show(diff.TotalMilliseconds.ToString()+" "+getNum1);

        }
            static decimal CustomParseDecimal(string input)
            {
                long n = 0;
                int decimalPosition = input.Length;
                for (int k = 0; k < input.Length; k++)
                {
                    char c = input[k];
                    if (c == '.')
                        decimalPosition = k+1;
                    else
                        n = (long)(n * 10) + (int)(c - '0');
                }
                return new decimal((int)n, (int)(n >> 32), 0, false, (byte)(input.Length - decimalPosition));
            }
            static int CustomParseInt(string input)
            {
                int n = 0;
                for (int k = 0; k < input.Length; k++)
                {
                    char c = input[k];
                    n = (n * 10) + (int)(c - '0');
                }
                return n;
            }

Converting decimal to double seems to have the least overhead. Every other change I made ended up being slower.

 
0
 

Thanks, the "CustomParseInt" performed 300% faster than Convert.ToInt32() which is great news!

However, instead of casting the decimal to a double. I might still wonder if
we can create a function that from the beginning returns a double as the casting
decrease the speed quite much.

If we can modify the function itself to return a double instead of a decimal?

static double CustomParseDouble(string input)
{
    //??
}
 
0
 

I've tried several ways but the problem is, it takes extra code. This is the tightest one I could come up with:

                double n = 0;
                double factor = 1;
                int flag=10;
                for (int k = 0; k < input.Length; k++)
                {
                    char c = input[k];
                    if (c == '.')
                        flag = .1;
                    else
                        factor *= .1 * flag;
                        n = (long)(n * 10) + (int)(c - '0');
                }
                return n;

Thisd one was also fairly good

                double n = 0;
                int decimalPosition = input.Length;
                for (int k = 0; k < input.Length; k++)
                {
                    char c = input[k];
                    if (c == '.')
                        decimalPosition = k+1;
                    else
                        n = (long)(n * 10) + (int)(c - '0');
                }
                return n/Math.Pow(10,input.Length-decimalPosition);

The basic algorithm returns an integer. The problem is determining where the decimal goes. In the second example I thought it would be fairly simple, divide n by 10 to the power determined by the difference of the total length and the decimal position. Apparently math is not C#'s strong suit, it took significantly longer, still shorter than converting the string to double but longer than converting the decimal to double. I then thought of doing the math in the algorithm itself. It works but the extra isntructions take up quite a bit of extra time.

 
0
 

Thanks for your help,

I found out by getting the length of the input string only 1 time instead of 3 times also saved 600 ms
like this:

        static double CustomParseDouble(string input)
        {

            double n = 0;

            int decimalPosition = input.Length;
            int length = decimalPosition;

            for (int k = 0; k < length; k++)
            {
                char c = input[k];

                if (c == '.')
                    decimalPosition = k + 1;
                else
                    n = (long)(n * 10) + (int)(c - '0');

            }
            return n / Math.Pow(10, length - decimalPosition);
        }
 
0
 

Great idea. Moving finding the length right out of the loop and passing the length as a parameter to the conversion routines shaved off a few more hundred ms. However I found the math.pow to be very slow. Using the original decimal and convert.todouble in the subroutine, I found it to be over twice as fast as the math.pow. Here's what I used:

public ConversionTest()
        {
            String number1 = "21.58";
            String number2 = "21";
            int num1length = number1.Length;
            int num2length = number2.Length;
            double getNum1 = 0;
            Int32 getNum2 = 0;

            DateTime dt = DateTime.Now;
            int runs = 85000 * 15 * 10;
            for (int s = 0; s < runs; s++)
            {               
                getNum1 = CustomParseDouble(number1,num1length);
                getNum2 = CustomParseInt(number2,num2length);
            }
            DateTime dt2 = DateTime.Now;
            TimeSpan diff = dt2 - dt;
            MessageBox.Show(diff.TotalMilliseconds.ToString());

        }
        static double CustomParseDouble(string input,int length)
        {
            long n = 0;
            int decimalPosition = length;
            for (int k = 0; k < length; k++)
            {
                char c = input[k];
                if (c == '.')
                    decimalPosition = k + 1;
                else
                    n = (long)(n * 10) + (int)(c - '0');
            }
            return Convert.ToDouble(new decimal((int)n, (int)(n >> 32), 0, false, (byte)(length - decimalPosition)));
        }
            static int CustomParseInt(string input,int length)
            {                
                int n = 0;
                int decimalPosition = length;
                for (int k = 0; k < length; k++)
                {
                    char c = input[k];
                    n = (n * 10) + (int)(c - '0');
                }
                return n;
            } 
 
0
 

It was a great idéa to pass the length to the function.
However it will not work with a real scenario as different strings will be passed
for conversion and .Length will be needed to to retreive each time.

I did a test of what we got now. I tried each test 3 times in a row to be quite sure
that the "ms" shows about the same each time and came up with that casting to a double
is the fastest conversion with 2062 ms.

But the decimal itself with 1437 ms is an increase in speed again. I just cant stop wonder if there is a way to return a double straight
away without needing to cast it, with the speed as the decimal.
I am thinking like crazy but conversions like this is the first time I am doing :)

Here are the tests so far:

        static decimal CustomParseDecimal(string input)
        {
            long n = 0;
            int decimalPosition = input.Length;
            int length = decimalPosition;

            for (int k = 0; k < length; k++)
            {
                char c = input[k];
                if (c == '.')
                    decimalPosition = k + 1;
                else
                    n = (n * 10) + (int)(c - '0');
            }
            return new decimal((int)n, (int)(n >> 32), 0, false, (byte)(length - decimalPosition));
        }



                String number1 = "21.58";
                String number2 = "21";
                decimal getNum1 = 0;
                double getNum2 = 0;


                DateTime dt = DateTime.Now;
                int runs = 85000 * 15 * 10;
                for (int s = 0; s < runs; s++)
                {
                    //Convert To Double
                    //getNum1 = CustomParseDecimal(number1); //1437 ms
                    //getNum2 = (double)CustomParseDecimal(number1); //2062 ms
                    //getNum2 = Convert.ToDouble(CustomParseDecimal(number1)); //2296 ms
                    //getNum2 = Convert.ToDouble(number1); //3812 ms
                }
                DateTime dt2 = DateTime.Now;
                TimeSpan diff = dt2 - dt;

                MessageBox.Show(diff.TotalMilliseconds.ToString());
 
0
 

I did succed to exchange the Math.Pow with the below for loop. When doing we are down to 1375 ms from 2062 ms when casting to a (double).
I also increased the speed with additionaly 100 ms by putting the if/else statement in the opposite order: if( c != '.') as that criteria actually will happen all but 1 case where the decimal is. By that the else statement wont execute.

This seems to be great news. I do wonder if it seems correct, what I have done or if I am missing anything. I have not complete control
of the code here.

The thing that bother me is that if we try to convert this string: "String number1 = "21.58123456789123456789123456789";"

The result will look like this: "2.48505795496617E-11"

This is the only thing that bother me, I wonder why that is happening?

        static double CustomParseDecimal(string input)
        {
            float n = 0;
            int decimalPosition = input.Length;
            int length = decimalPosition;


            for (int k = 0; k < length; k++)
            {
                char c = input[k];
                if (c != '.')
                    n = (n * 10) + (int)(c - '0');                    
                else
                    decimalPosition = k + 1;
            }

            float x = 10;
            for (int i = 0; i < length - decimalPosition - 1; i++)
            {
                x = x * 10;
            }
            return n / x;
        }





                String number1 = "21.58";
                String number2 = "21";
                decimal getNum1 = 0;
                double getNum2 = 0;


                DateTime dt = DateTime.Now;
                int runs = 85000 * 15 * 10;
                for (int s = 0; s < runs; s++)
                {
                    //Convert To Double
                     getNum2 = CustomParseDecimal(number1); //1625 ms
                }
                DateTime dt2 = DateTime.Now;
                TimeSpan diff = dt2 - dt;

                MessageBox.Show(diff.TotalMilliseconds.ToString());
 
0
 

By definition 'double' in c# is only precise to 15-16 digits and 'float' is only precise to 7 digits. Even a decimal is only 28-29 digits and your number is 31. If you need to handle numbers that large you'll have to use someone elses library designed for that or program your own.

 
0
 

Yes, you are right. I did just try with so many decimals. It wont be any problem as the double values itselfs will be stored in string and by themselves have the correct decimal places.

Thank you for pointing me out with the float. I have exchanged them to double again.
So we have the speed: 1850 ms. Not so bad anyway. An increase of > 100% actually.

Thank you for your help

You
This question has already been solved: Start a new discussion instead
Post:
Start New Discussion
Tags Related to this Article