简体   繁体   中英

C# Double.ToString() performance issue

I have the following method to convert a double array to a List<string> :

    static Dest Test(Source s)
    {
        Dest d = new Dest();

        if (s.A24 != null)
        {
            double[] dd = s.A24;

            int cnt = dd.Length;

            List<string> lst = new List<string>();

            for (int i = 0; i < cnt; i++)
                lst.Add(((double)dd[i]).ToString());

            d.A24 = lst;
        }
        else
        {
            d.A24 = null;
        }

        return d;
    }

Doing a List.Add() in a loop seems like the fastest way according to my benchmarks beating all the various LINQ and Convert tricks.

This is really slow. 2400ms for a million calls (Any CPU, prefer 64-bit). So I was experimenting with various ways to make it faster. Let's assume I cannot cache the source or dest lists, etc obviously.

So anyways, I stumbled across something weird here... if I change the lst.Add() line to cast to a decimal instead of a double, it is much, MUCH faster. 900ms vs 2400ms.

Here's my questions

1) decimal has greater accuracy then double, so I shouldn't lose anything in the type cast, correct?

2) why is the Decimal.ToString() so much faster then Double.ToString()?

3) is this a reasonable optimization, or am I missing some key detail where this will come back to bite me?

I'm not concerned about using up a little bit more memory, I am only concerned about performance.

Nothing sophisticated for the test data at this point, just using:

s.A24 = new double[] { 1.2, 3.4, 5.6 };

For what it's worth, I ran the following and got different results, with decimal usually taking slightly longer (but both calls of the calls to lst.Add() and number.ToString() being roughly equivalent).

What type of collection is A24 in your code? I wouldn't be surprised if the additional overhead you're seeing is actually in casting or something you're not currently looking at.

var iterations = 1000000;

var lst = new List<string>();

var rnd = new Random();
var dblArray = new double[iterations];
for (var i = 0; i < iterations; i++)
    //INTERESTING FINDING FROM THE COMMENTS
    //double.ToString() is faster if this line is rnd.NextDouble()
    //but decimal.ToString() is faster if hard-coding the value "3.5" 
    //(despite the overhead of casting to decimal)
    dblArray[i] = rnd.NextDouble();

var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < iterations; i++)
    lst.Add(dblArray[i].ToString());
sw.Stop();
//takes 280-300 MS
Debug.WriteLine("Double loop MS: " + sw.ElapsedMilliseconds);

//reset list
lst = new List<string>();
sw.Restart();
for (var i = 0; i < iterations; i++)
    lst.Add(((decimal)dblArray[i]).ToString());
sw.Stop();
//takes 280-320 MS
Debug.WriteLine("Decimal loop MS: " + sw.ElapsedMilliseconds);

A Decimal and Double are often confused and interchanged, but they are completely different animals at the processor level. If I had to imagine writing the code to for Double.ToString(), I can see the problem... It's hard. Comparatively, writing the code for Decimal.ToString() shouldn't be much more difficult than Int32.ToString(). I'm sure if you compare Int32.ToString() to Decimal.ToString() you will find they results are very close.

FYI: Double and Float(Single) are not exact, and many numbers can't be expressed in a Double. In your example, you give 1.2 which is really 1 + 1/5. That can't exist as a true double (even if the VS IDE covers for it). You would get something like 1.1999999999999998. If you want performance, use a Decimal.

2) why is the Decimal.ToString() so much faster then Double.ToString()?

  1. Because Double.ToString() is actually much more complex. Compare the core implementation of Decimal.ToString() and Double.ToString() , Decimal.ToString() has fixed precision whereas Double.ToString() 's precision is on demand. Double has its IEEE floating point definition which is also much more complex than Decimal .

  2. Current Double.ToString() implementation relies on _ecvt on Windows and snprintf on Linux . They're inefficient (especially for Linux implementation). There's an in-progress PR to re-write Double.ToString() in an efficient way which removes the dependency of _ecvt and snprintf .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM