Почему double, а не decimal?
Atom
31.10.2010


Заинтересовал вопрос, почему параметры в double а не decimal, у которого фиксированная точка?



Спасибо:


< 1 2 3 4  >
Mikhail Sukhov

Фотография
Дата: 21.03.2011
Ответить


Иванов Андрей Перейти

Не хотите разбираться с представлением чисел на компьютере, просто попробуйте. У вас число в примере четыре знака, представляя его в виде double или decimal, результат не изменится. Такие числа можно хоть в float хранить, с семью знаками, ничего не изменится. Просто смысла нет -- рантайм может его запросто хранить в 64 битах, хоть он и 32 бита, и процессор его наверняка будет считать в 64 битах.


Дело то не в максимальной размерности и точности после запятой. А в точности результирующего значения. Ему верить нельзя.

Иванов Андрей Перейти

Если я неправ, покажите код или псевдокод, который приводит к 100.99999 в double и не приводит к тому же в decimal.


Да легко:

Код
var priceF = 109.1;

for (var i = 0; i < 9; i++)
{
    priceF += 0.1 * i;
}

priceF /= 0.1;

if (priceF != 1127)
    throw new Exception();


Код

var priceD = 109.1m;

for (var i = 0; i < 9; i++)
{
    priceD += 0.1m * i;
}

priceD /= 0.1m;

if (priceD != 1127)
    throw new Exception();
Спасибо:

Иванов Андрей

Фотография
Дата: 22.03.2011
Ответить


Поставьте не 0.1, а 0.01, посмотрите, что получится.
Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double.
Это особенности представления вещественных чисел на компьютере, плата за скорость.

Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки. И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.
Спасибо:

anothar

Фотография
Дата: 22.03.2011
Ответить


Иванов Андрей Перейти
Поставьте не 0.1, а 0.01, посмотрите, что получится.
Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double.
Это особенности представления вещественных чисел на компьютере, плата за скорость.

Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки. И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.

Вот неплохой пост:http://stackoverflow.com/questions/803225/when-should-i-use-double-instead-of-decimal. В целом он повторяет Ваши мысли и мысли Михаила. Смысл примерно такой что для финансовых вычислений скорее всего больше подходит именно decimal(кроме тех случаев когда у Вас очень большой расчет) по той причине, которую указал Михаил, а для всех остальных double(или float)-но это как я понял.
Спасибо:

Mikhail Sukhov

Фотография
Дата: 22.03.2011
Ответить


Иванов Андрей Перейти
Поставьте не 0.1, а 0.01, посмотрите, что получится.
Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double.
Это особенности представления вещественных чисел на компьютере, плата за скорость.


При торговле в реальном времени что double что decimal не несет никакой разницы в производительности. Но мне приходится каждый раз делать округление, так как я не могу гарантировать в коде, что double будет сформирован правильно. То, что при 0.01 мой пример работает, а при 0.1 не работает - это еще хуже, чем если бы не работало всегда. Я не могу однозначность сказать - все будет работать. Поэтому лишняя перестраховка в виде выравнивания цены.

Иванов Андрей Перейти

Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки.


Если я вычисляю сред арифметическое, то я точно знаю, что нужно округлять. И я точно знаю, что код, подобный моему примеру, не требует округления. Главное для меня, точно знать как будет работать. Я уже довольно много натыкался на грабли, когда код не работал банально из-за неточности double. Часами выискивал проблему, которая может и вообще сегодня не проявиться. А такая бага самая худшая, когда то работает, то нет (ваш пример с ИИС).

Иванов Андрей Перейти

И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.


Ее тяжесть начала проявляться на бэк тестинге. Если в реальном времени больше времени занимает получение данных, то в виртуальном времени - торговая часть.
Спасибо:

Иванов Андрей

Фотография
Дата: 22.03.2011
Ответить


Mikhail Sukhov Перейти
Иванов Андрей Перейти
Поставьте не 0.1, а 0.01, посмотрите, что получится.
Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double.
Это особенности представления вещественных чисел на компьютере, плата за скорость.


При торговле в реальном времени что double что decimal не несет никакой разницы в производительности. Но мне приходится каждый раз делать округление, так как я не могу гарантировать в коде, что double будет сформирован правильно. То, что при 0.01 мой пример работает, а при 0.1 не работает - это еще хуже, чем если бы не работало всегда. Я не могу однозначность сказать - все будет работать. Поэтому лишняя перестраховка в виде выравнивания цены.

В интернетах есть мнение, что стандарт IEEE 754 убьёт мир =)
http://www.yur.ru/science/computer/IEEE754.htm

Лишняя перестраховка в виде выравнивания цены не хуже лишней перестраховки в виде decimal =)

Цитата:

Иванов Андрей Перейти

Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки.


Если я вычисляю сред арифметическое, то я точно знаю, что нужно округлять. И я точно знаю, что код, подобный моему примеру, не требует округления. Главное для меня, точно знать как будет работать. Я уже довольно много натыкался на грабли, когда код не работал банально из-за неточности double. Часами выискивал проблему, которая может и вообще сегодня не проявиться. А такая бага самая худшая, когда то работает, то нет (ваш пример с ИИС).

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

Цитата:

Иванов Андрей Перейти

И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.


Ее тяжесть начала проявляться на бэк тестинге. Если в реальном времени больше времени занимает получение данных, то в виртуальном времени - торговая часть.

У меня половину времени занимает чтение лога сделок из файла. На истории [20 декабря, сейчас] по GMKN всё примерно 5.5 секунд, из них секунды 3 это чтение истории из файлов, мегабайтов 90 где-то. Для одного дня программа дольше запускается, чем работает. Вполне возможно, что я использую какие-то очень простые инструменты.

На decimal уже переделали или только планы? Любопытно было бы узнать, как они работают против double. То есть, прогнать одну и ту же стратегию с double и с decimal на месяце сделок. И скорость интересна, и прирост денег на торговле 1 млн без плеча. Если мой способ не подходит, то какой хотите использовать для оценки эффективности перехода? Вам же надо как-то оценивать вложенный труд.

Уверен, что decimal не принесёт денег, зато принесёт хлопоты.
Спасибо:

Mikhail Sukhov

Фотография
Дата: 22.03.2011
Ответить


Иванов Андрей Перейти
На decimal уже переделали или только планы? Любопытно было бы узнать, как они работают против double. То есть, прогнать одну и ту же стратегию с double и с decimal на месяце сделок. И скорость интересна, и прирост денег на торговле 1 млн без плеча. Если мой способ не подходит, то какой хотите использовать для оценки эффективности перехода? Вам же надо как-то оценивать вложенный труд.

Уверен, что decimal не принесёт денег, зато принесёт хлопоты.


Я уже переделал до 3.0 еще. Но это не выносил в публичную ветку. Получилось быстрее, потому что из своей стратегии я повыкидывал Shrink.
Спасибо:

Иванов Андрей

Фотография
Дата: 26.03.2011
Ответить


Андрей Ефимов Перейти
Иванов Андрей Перейти
Поставьте не 0.1, а 0.01, посмотрите, что получится.
Угадаете, чему будет равно Math.Round(2.5)? А вы в ShrinkPrice используете Math.Round =) Причина проскальзывания скорее здесь, а не в использовании double.
Это особенности представления вещественных чисел на компьютере, плата за скорость.

Я не понимаю, чем именно вас спасёт представление 0.1 как 0.1, а не как 0.10000000000000001 -- получение среднего арифметического двух цен, например, приводит к ошибке округления, которая всё равно заставляет использовать вашу функцию при выставлении заявки. И не такая уж она тяжёлая, эта ShrinkPrice, потому что выставлять заявки надо очень редко по сравнению с тем, сколько надо считать до выставления заявки.

Вот неплохой пост:http://stackoverflow.com/questions/803225/when-should-i-use-double-instead-of-decimal. В целом он повторяет Ваши мысли и мысли Михаила. Смысл примерно такой что для финансовых вычислений скорее всего больше подходит именно decimal(кроме тех случаев когда у Вас очень большой расчет) по той причине, которую указал Михаил, а для всех остальных double(или float)-но это как я понял.

Не совсем так. "Финансы" это слишком обще.

Когда вам надо суммировать операции по счетам клиентов, то выбора нет, только decimal. Потому что представить в double 0.1 (10 копеек), например, невозможно. Когда у вас есть арифметика (считаете проценты, например), то double обычно точнее и всегда значительно быстрее. В случае с роботами суммирование операций это вспомогательные вычисления, основные как раз разнообразная арифметика. Мне так кажется =)

По сути, это вопрос религии или требования к производительности -- сложив три раза 1/3 в decimal получишь 0.9999999999, а в double 1.0, но сложив 10 раз 0.1 для decimal будет 1.0 и 0.999999999 для double. То есть, округлять надо независимо от выбора представления и результат будет приемлемый в любом случае. А если бездумно делить, повторюсь в который раз, decimal не поможет.

Mikhail Sukhov Перейти
Иванов Андрей Перейти
На decimal уже переделали или только планы? Любопытно было бы узнать, как они работают против double. То есть, прогнать одну и ту же стратегию с double и с decimal на месяце сделок. И скорость интересна, и прирост денег на торговле 1 млн без плеча. Если мой способ не подходит, то какой хотите использовать для оценки эффективности перехода? Вам же надо как-то оценивать вложенный труд.

Уверен, что decimal не принесёт денег, зато принесёт хлопоты.


Я уже переделал до 3.0 еще. Но это не выносил в публичную ветку. Получилось быстрее, потому что из своей стратегии я повыкидывал Shrink.

Хорошо. "Посмотрим, что скажет стая" =)
Интересно будет послушать про результаты -- стало быстрее и/или больше денег. Может быть выложить параллельно версию с decimal?
Спасибо:

Иванов Андрей

Фотография
Дата: 26.03.2011
Ответить


Исходя из описания и именования, я бы ShrinkPrice сделал вот таким.
Проверить мне его не на чем, но не вижу причин, почему бы ему не работать в несколько десятков раз быстрее.

Было
Код

public static double ShrinkPrice(this Security security, double price, ShrinkRules rule = ShrinkRules.Auto)
{
if (security == null)
throw new ArgumentNullException("security");
decimal num = (decimal) price;
decimal minStepSize = (decimal) security.MinStepSize;
if (minStepSize == 0M)
throw new ArgumentException("trololo");
return (double) num.Round(minStepSize, security.Decimals, ((rule == ShrinkRules.Auto) ? null : new MidpointRounding?((rule == ShrinkRules.Less) ? MidpointRounding.AwayFromZero : MidpointRounding.ToEven)));
}

Стало
Код

public static double ShrinkPrice(this Security security, double price, ShrinkRules rule = ShrinkRules.Auto)
{
    if (security == null)
        throw new ArgumentNullException("security");
    if (price <= 0.0)
        throw new ArgumentException("Получена цена меньше либо равная нулю.", "price");
    if (security.MinStepSize <= 0.0)
        throw new InvalidOperationException("Невозможно выровнять цену для инструмента без установленного минимального шага цены."); // security.MinStepSize не параметр метода
    price /= security.MinStepSize;
    if (rule == ShrinkRules.Auto)
        price = Math.Round(price, MidpointRounding.AwayFromZero);
    else if (rule == ShrinkRules.More)
        price = Math.Ceiling(price);
    else if (rule == ShrinkRules.Less)
        price = Math.Floor(price);
    else
        throw new ArgumentOutOfRangeException("rule", rule, "Получено неизвестное правило округления.");
    price *= security.MinStepSize;
    return price;
}

Спасибо:

anothar

Фотография
Дата: 26.03.2011
Ответить


Вот комментарий по производительности:Decimal perfomance
Если ему верить, то у decimal производительность не ахти. А что если внутри представлять все ввиде целого Int64? Ну а наружу-без разницы. ТО есть по сути будет такая структура: фиксированное число знаков после точки, которое зависит только от инструмента и значение ввиде Int64(например 0.0012-это четыре знака после точки и значение 12). Если Вы преимущественно складываете, делите и т.д в рамках одного инструмента то все ок, но насколько это извращенно? По сути это и есть идея того поста.
Спасибо:

Иванов Андрей

Фотография
Дата: 27.03.2011
Ответить


Это не будет быстрее double =) Зато будет больше места для ошибок. И голову надо под такое перестраивать, как под ассемблер.

Если вам не нужна точность за вашими знаками (среднее арифметическое между 0.0012 и 0.0015 при использовании long что даст?), double будет работать не хуже. Чтобы накопить в double ошибку в четвёртом знаке, надо несколько миллионов или десятков миллионов операций. Ошибка быстро накапливается, если используются значения из разных диапазонов -- цена какого-нибудь ИнтерРАО с лотом 100 тысяч и объём торгов за час. То есть, отличаться они должны на несколько порядков. Чем больше порядков различие, тем быстрее накапливается ошибка.
Спасибо:
< 1 2 3 4  >

Добавить файлы через драг-н-дроп, , или вставить из буфера обмена.

loading
clippy