Проблема с С# Double.ToString()

У меня есть следующий метод для преобразования двойного массива в 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;
    }

Выполнение List.Add() в цикле кажется самым быстрым способом в соответствии с моими критериями, избивающими все различные LINQ и Convert трюки.

Это очень медленно. 2400 мс для миллиона вызовов (любой процессор, предпочитающий 64-разрядный). Поэтому я экспериментировал с различными способами, чтобы сделать это быстрее. Предположим, что я не могу кэшировать исходные или списки исключений и т.д., Очевидно.

Так или иначе, я наткнулся на что-то странное здесь... если я изменил строку lst.Add(), чтобы отличить десятичную, а не двойную, это намного, МНОГО быстрее. 900мс против 2400 мс.

Здесь мои вопросы

1) десятичная имеет большую точность, а затем двойную, поэтому я не должен ничего потерять в типе cast, правильно?

2) почему Decimal.ToString() намного быстрее, чем Double.ToString()?

3), это разумная оптимизация, или я пропустил некоторые ключевые детали, где это вернется, чтобы укусить меня?

Я не беспокоюсь о том, чтобы использовать немного больше памяти, меня беспокоит только производительность.

Ничего сложного для тестовых данных на данный момент, просто используя:

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

Ответ 1

Для чего это стоит, я запускал следующее и получал разные результаты: десятичное значение обычно занимает немного больше (но оба вызова вызовов в lst.Add() и number.ToString() примерно эквивалентны).

Какой тип коллекции A24 в вашем коде? Я не удивлюсь, если дополнительные накладные расходы вы видите на самом деле в кастинге или что-то, что вы в настоящее время не смотрите.

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);

Ответ 2

Десятичные и двойные часто путают и меняют местами, но они совершенно разные животные на уровне процессора. Если бы мне пришлось представить код для Double.ToString(), я вижу проблему... Это сложно. Сравнительно, писать код для Decimal.ToString() не должно быть намного сложнее, чем Int32.ToString(). Я уверен, что если вы сравните Int32.ToString() с Decimal.ToString(), вы обнаружите, что результаты очень близки.

FYI: Double и Float (Single) не являются точными, и многие числа не могут быть выражены в Double. В вашем примере вы даете 1.2, который действительно 1 + 1/5. Это не может существовать как истинный двойной (даже если VS IDE покрывает его). Вы получите что-то вроде 1.1999999999999998. Если вам нужна производительность, используйте десятичный знак.

Ответ 3

2) почему Decimal.ToString() намного быстрее, чем Double.ToString()?

  • Потому что Double.ToString() на самом деле намного сложнее. Сравните основную реализацию Decimal.ToString() и Double.ToString(), Decimal.ToString() имеет фиксированную точность, тогда как точность Double.ToString() - по требованию. Double имеет определение с плавающей точкой IEEE, которое также намного сложнее, чем Decimal.

  • Текущая реализация Double.ToString() основана на _ecvt на Windows и snprintf на Linux. Они неэффективны (особенно для реализации Linux). Там в процессе PR переписывать Double.ToString() эффективным способом, который устраняет зависимость _ecvt и snprintf.