Возник вопрос на стыке проектирования, UX и БД )) Требуются ваши мнения.
Мне нужно спроектировать DSL. С одной стороны, он должен быть строго типизирован (это требование), а с другой — этот тип появится на уровне GUI для массовых пользователей. А их, по возможности, хотелось бы не грузить разницей между int и double (не говоря про потерю точности), а дать им вместо этого некий универсальный тип number. С другой стороны, возможно, стоит один раз научить юзеров разнице между int и double, чем разруливать миллион неоднозначностей.
Что еще хуже, иногда возможно хранение в переменных сумм денег. Как я знаю, в таких случаях принято использовать третий тип: с эмуляцией вещественности через целые (нужны гарантии, что 0.1 + 0.2 == 0.3). Но три разных типа для чисел — для юзеров это уже будет перебор. Или нет? Может, как раз дать им кучу типов и пусть сами несут ответственность за сходимость балансов? ))
Сами значения будут храниться в sqlite (он лёгкий, встраиваемый и опенсорсный, но если есть альтернативы, можно рассмотреть). Доступ к ним планируется только через слой этого DSL'я, хотя, конечно же, желательно сохранить возможность обработки (в т.ч. вычислений) через запросы. Я так понимаю, придётся все агрегирующие функции писать самому?
Итак, что посоветуете для внутреннего представления чисел в памяти и хранения в sqlite? И какими типами это всё оформить для юзеров?
Здравствуйте, Alekzander, Вы писали:
A>Итак, что посоветуете для внутреннего представления чисел в памяти и хранения в sqlite? И какими типами это всё оформить для юзеров?
На вскидку я бы ещё тип percent рассмотрел. А вообще, с бы этот вопрос задал тем, кто шарит в 1C. Всё же там накоплен ахренительно немалый опыт по данной теме.
По поводу sqlite, то желательно подробности. Если предполагается минимум данных и нежелательность возни с установками дополнительного софта, то sqlite нормальный выбор.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Alekzander, Вы писали:
A>Мне нужно спроектировать DSL. С одной стороны, он должен быть строго типизирован (это требование), а с другой — этот тип появится на уровне GUI для массовых пользователей. А их, по возможности, хотелось бы не грузить разницей между int и double (не говоря про потерю точности), а дать им вместо этого некий универсальный тип number. С другой стороны, возможно, стоит один раз научить юзеров разнице между int и double, чем разруливать миллион неоднозначностей.
A>Что еще хуже, иногда возможно хранение в переменных сумм денег. Как я знаю, в таких случаях принято использовать третий тип: с эмуляцией вещественности через целые (нужны гарантии, что 0.1 + 0.2 == 0.3). Но три разных типа для чисел — для юзеров это уже будет перебор. Или нет? Может, как раз дать им кучу типов и пусть сами несут ответственность за сходимость балансов? ))
A>Сами значения будут храниться в sqlite (он лёгкий, встраиваемый и опенсорсный, но если есть альтернативы, можно рассмотреть). Доступ к ним планируется только через слой этого DSL'я, хотя, конечно же, желательно сохранить возможность обработки (в т.ч. вычислений) через запросы. Я так понимаю, придётся все агрегирующие функции писать самому?
Здравствуйте, Alekzander, Вы писали:
A>Мне нужно спроектировать DSL. С одной стороны, он должен быть строго типизирован (это требование), а с другой — этот тип появится на уровне GUI для массовых пользователей. А их, по возможности, хотелось бы не грузить разницей между int и double (не говоря про потерю точности), а дать им вместо этого некий универсальный тип number. С другой стороны, возможно, стоит один раз научить юзеров разнице между int и double, чем разруливать миллион неоднозначностей.
Можно сделать некий универсальный тип int и некий универсальный тип float. Но объединить не получится, у них слишком разные свойства. И эта разница в свойствах, она существенна при выборе типа.
От этого можно отступить, только если все эти тонкости пользователям не важны. Как в JavaScript, например; там все числа — это 64-битный floating point, но известно, что в довольно широком диапазоне целые в нем представляются без потерь точности, поэтому их можно использовать в качестве счетчиков циклов и т.п., особо не задумываясь.
А вот важны эти тонкости пользователям или нет — зависит от самих пользователей и от решаемых ими задач. И это надо как-то с ними обсудить.
A>Что еще хуже, иногда возможно хранение в переменных сумм денег. Как я знаю, в таких случаях принято использовать третий тип: с эмуляцией вещественности через целые (нужны гарантии, что 0.1 + 0.2 == 0.3). Но три разных типа для чисел — для юзеров это уже будет перебор. Или нет? Может, как раз дать им кучу типов и пусть сами несут ответственность за сходимость балансов? ))
Fixed point. Это когда счет идет, например, в сотых. Или в тысячных. Но конкретный выбор фиксируется на уровне типа и не меняется в процессе рассчетов, в отличии от float. Можно объединить с целыми, они — частный случай фиксированной точки.
A>Итак, что посоветуете для внутреннего представления чисел в памяти и хранения в sqlite? И какими типами это всё оформить для юзеров?
Я бы шел от пользовательского интерфейса (который, в данном случае — твоя система типов, видимая пользователям). А когда там устаканится, уже думал бы о деталях реализации.
Здравствуйте, IT, Вы писали:
IT>На вскидку я бы ещё тип percent рассмотрел.
Спасибо за напоминание, это хорошая идея. Но я думаю, это сугубо вторичная вещь, которую как тип оформлять вообще не надо. На уровне GUI можно сделать галку "Это проценты". А на уровне DSL... лет двадцать назад мой друг писал эвалюатор математических выражений. Он сделал очень просто: заменял знак % на "/100.0" где это было синтаксически уместно. Можно было написать pi*50% и получить 1.57... Как выяснилось за двадцать лет использования, этот трюк покрывает все юзерские хотелки.
IT>По поводу sqlite, то желательно подробности. Если предполагается минимум данных и нежелательность возни с установками дополнительного софта, то sqlite нормальный выбор.
Так а какие подробности? Все данные имеют характер табличных и хранятся в таблицах. Впрочем, вот две важных подробности. Первая. Нужно, чтобы это собиралось, работало и не тормозило на недорогом телефоне с Android. Тут, я думаю, проблем не будет — в одной компании, где я работал 10 лет назад, sqlite использовали ещё под WinMo на ARM'ах тех времён. Вторая. Нужно много вычислений по формулам (как для каждой строки в вычисляемых колонках, так и для таблицы в целом — min/max/avg/etc). Хранить ли вычисленное и перевычислять из триггеров, или вычислять всё каждый раз на лету — я ещё не решил, мне, так или иначе, понадобится написать eval(), а во втором случае желательно иметь удобный способ его вызова, инициируемый из запроса.
Я правильно понимаю, что ты посоветовал использовать number, и предложил имплементацию? Если да, можно о ней хотя бы в двух словах рассказать, в частности о том, как сериализовать значения (записывать в sqlite)?
Здравствуйте, Alekzander, Вы писали:
vsb>>Посоветую взять JavaScript и всё. Число это просто double (64-битная плавающая точка).
A>Image: firefox_K8XYZyZaEJ.png
Требование про 0.3 не несёт смысла, поэтому я его даже не рассматриваю. Если хочешь обсудить — расскажи подробней, зачем тебе такое требование и какую задачу ты хочешь с его помощью решать. Пока у тебя нет деления, проще всего просто представлять все числа в копейках (или сотых долях копейки, или тебе видней, какая у тебя гранулярность планируется), в double 64 можно засунуть 52-битовое целое число, такого размера хватит для всех разумных целей. Если есть деление — проще с округлением аккуратно работать. Опять же точности double хватит для подавляющего большинства случаев на практике.
Здравствуйте, Alekzander, Вы писали:
A>Так а какие подробности? Все данные имеют характер табличных и хранятся в таблицах. Впрочем, вот две важных подробности. Первая. Нужно, чтобы это собиралось, работало и не тормозило на недорогом телефоне с Android.
sqlite в андроиде примерно в каждой аппе прикручен. Например, whatsapp хранит базу сообщений в нём
Здравствуйте, vsb, Вы писали:
A>>Image: firefox_K8XYZyZaEJ.png
vsb>Требование про 0.3 не несёт смысла, поэтому я его даже не рассматриваю. Если хочешь обсудить — расскажи подробней, зачем тебе такое требование и какую задачу ты хочешь с его помощью решать. Пока у тебя нет деления, проще всего просто представлять все числа в копейках (или сотых долях копейки, или тебе видней, какая у тебя гранулярность планируется),
А потом захочется другую валюту, с другим количеством условных копеек в условном рубле, и вся эта схема накроется пимедным тзом.
vsb>в double 64 можно засунуть 52-битовое целое число, такого размера хватит для всех разумных целей. Если есть деление — проще с округлением аккуратно работать. Опять же точности double хватит для подавляющего большинства случаев на практике.
В double денег влезает, на самом деле не очень много. Хотя, для домашней бухгалтерии, может и хватит.
Но вообще, хранить деньги в double — очень, очень плохая идея.
Здравствуйте, vsb, Вы писали:
M>>В double денег влезает, на самом деле не очень много. Хотя, для домашней бухгалтерии, может и хватит.
vsb>45 триллионов рублей, если считать с копейками. Неплохая у вас домашняя бухгалтерия.
Попиливал бухгалтерию для торговли на бирже. Прикинул, что вполне могут произойти ситуации, какие-нибудь агрегированные расчеты за года, когда не влезу
Здравствуйте, vsb, Вы писали:
M>>В double денег влезает, на самом деле не очень много. Хотя, для домашней бухгалтерии, может и хватит.
vsb>45 триллионов рублей, если считать с копейками. Неплохая у вас домашняя бухгалтерия.
Кстати, некоторые акции торгуются по ценам с шестью знаками после точки (в рублях). 640двух знаков под копейки хватит всем, ага
Здравствуйте, Marty, Вы писали:
M>>>В double денег влезает, на самом деле не очень много. Хотя, для домашней бухгалтерии, может и хватит.
vsb>>45 триллионов рублей, если считать с копейками. Неплохая у вас домашняя бухгалтерия.
M>Попиливал бухгалтерию для торговли на бирже. Прикинул, что вполне могут произойти ситуации, какие-нибудь агрегированные расчеты за года, когда не влезу
Очень сомневаюсь, что потеря нижних битов в таких отчётах на что-то повлияет. Типа мы за 33 года имели оборот не 50 123 456 789 111 а 50 123 456 789 121 рублей. Обычно такие числа даже на выводе обрезают, чтобы читать можно было.
Ну и в конце концов для такой специализированной задачи можно и BigInt взять конкретно в этом отчёте.
Здравствуйте, vsb, Вы писали:
M>>Попиливал бухгалтерию для торговли на бирже. Прикинул, что вполне могут произойти ситуации, какие-нибудь агрегированные расчеты за года, когда не влезу
vsb>Очень сомневаюсь, что потеря нижних битов в таких отчётах на что-то повлияет. Типа мы за 33 года имели оборот не 50 123 456 789 111 а 50 123 456 789 121 рублей. Обычно такие числа даже на выводе обрезают, чтобы читать можно было.
А я сомневаюсь, что это можно просто проигнорировать
vsb>Ну и в конце концов для такой специализированной задачи можно и BigInt взять конкретно в этом отчёте.
Зачем в разных отчетах использовать разные типы? Если вкорячивать BigInt/BigNumber, почему сразу их везде не использовать и не беспокоится, что где-то что-то в какой-то момент пропадёт?
Здравствуйте, Marty, Вы писали:
M>Зачем в разных отчетах использовать разные типы? Если вкорячивать BigInt/BigNumber, почему сразу их везде не использовать и не беспокоится, что где-то что-то в какой-то момент пропадёт?
Потому, что double работает очень быстро, а bigint работает очень медленно. Потому, что double поддерживается практически всем софтом, а bigint — далеко не всем, в частности пресловутым sqlite не поддерживается. Делать софт медленней на порядок-два ради мнимых юзкейсов я считаю неправильным.
Здравствуйте, vsb, Вы писали:
vsb>Потому, что double работает очень быстро, а bigint работает очень медленно.
Очень медленно — это насколько медленно?
У меня очень медленно только деление работает, остальные операции медленно, но уже не очень. +/- работают в 20 раз медленее, умножение — в 50 раз. Это медленно, конечно, но не прям уж так. Если мы не САПР/3D пишем, этим можно пренебречь.
И, в принципе, это ещё можно раза в два ускорить — у меня по байту на десятичный знак, а можно упаковать два знака в байт, это будет побыстрее, но мне лень было, писать несколько посложнее бы было
vsb>Потому, что double поддерживается практически всем софтом, а bigint — далеко не всем,
Что значит — поддерживается?
vsb>в частности пресловутым sqlite не поддерживается.
В частности в пресловутом sqlite внезапно всё хранится строками, а вот всякие обёртки, уже да, любят в дабл конвертить
vsb>Делать софт медленней на порядок-два ради мнимых юзкейсов я считаю неправильным.
Софт не будет медленнее на порядки, потому что операции с этими числами там будут занимать доли процентов от общего времени работы. Никто этого не заметит. А вот гарантии того, что в числе ни один знак не пропадёт и не изменится — в некоторых случаях дорогого стоят. У BigDecimal есть ещё приятная особенность, что можно числа прямо сравнивать, и не приседать с точностью представления.
И ещё по поводу скорости. У меня есть интерфейс рисования, обёртка над GDI и GDI+, и вот там я сделал так, что можно везде использовать double, а можно мой Decimal. Так вот, отрисовка сцены не отличается ни на милисекунды при использовании того или другого, всё равно всё время сжирают вызовы GDI, нужно очень огромные сцены рисовать, с сотнями/тысячами объектов, чтобы тип чисел начал как-то влиять.
А вообще, тех, кто считает деньги в double — их надо сразу расстреливать
Здравствуйте, Marty, Вы писали:
M>Софт не будет медленнее на порядки, потому что операции с этими числами там будут занимать доли процентов от общего времени работы. Никто этого не заметит. А вот гарантии того, что в числе ни один знак не пропадёт и не изменится — в некоторых случаях дорогого стоят.
Ключевое выделил. Твой пример с биржой и 6 знаками после запятой, вероятно, относится к этим некоторым случаям. 99% остальных юз-кейсов, вероятно, не относятся к этим случаям.
M>У BigDecimal есть ещё приятная особенность, что можно числа прямо сравнивать, и не приседать с точностью представления.
Это пока у тебя деления нет в алгоритмах. Когда появится — там и до рациональных чисел дойти, возможно, придётся. А то и до символьных вычислений.
M>А вообще, тех, кто считает деньги в double — их надо сразу расстреливать