объяснять придется... 🧐
Есть целочисленные типы - инты. В финансах в здравом уме их никто не юзает кроме как в качестве идентификаторов, т.к. постоянно нужно вылавливать копейки.
Есть числа с плавающей точкой. С ними почти все хорошо, можно ловить и микро и нано-копейки, только есть одна беда:
#include <stdio.h>
int main(int argc, char const *argv[])
{
float a = 0.1 + 0.1 + 0.1;
if(a == 0.3) printf("a равно 0.3\n");
else printf("a не равно 0.3\n");
return 0;
}
Что выдаст? По логите true, на практике хрен там! И в паскале тоже самое, только проверил:
program Project1;
var
a : real;
begin
a := 0.1 + 0.1 + 0.1;
if a = 0.3 then begin
writeln('Равно.');
end
else begin
writeln('Не равно!');
end;
end.
Это специфика чисел с плавающей точкой. И что бы не стрелять себе по ногам придумали сложные/составные числовые типы. В базах это number в оракле, numeric в постгресе, money в mysql и ms sql и т.д..
И currency как раз тоже такой тип, проверил:
program Project1;
var
a : Currency;
begin
a := 0.1 + 0.1 + 0.1;
if a = 0.3 then begin
writeln('Равно.');
end
else begin
writeln('Не равно!');
end;
end.
Но такие типы считаются медленно, поэтому в базах при длительных расчетах иногда преобразовываем данные к флотам, считаем, затем обратно к денежным.
медленно работает bigint/bigfloat - где кол-во разрядов может быть оч большим/меняться, из-за чего подсчеты происходят "софтверно", с fixed-point же нет проблем - для процессора это как работа с обычными интами, ток на переводе - туда - обратно - деление/умножение.
А конкретно к какому сообщению или заявлению оппонируешь этим сообщением? Так-то ок. Познавательно. Что-то и новое узнал. Хотя с финансовыми программами дела не имел )
так паскалевское Currency и есть число с фиксированной точкой, в чём проблема?
Звездочка U1202D, сообщения читаешь или только себя?)
Проблема только одна - она (точка) очень фиксированная. Всегда 4 знака после запятой. Если вдруг понадобится точнее - тады ой.
У нас пытались 18.2, но что - то не прижилось
Все почти верно, только наоборот. В этом можете сами спокойно убедиться, набросал для постгреса: do $$ declare t_start timestamp; t_stop timestamp; --my_number double precision; my_number numeric; begin t_start := clock_timestamp(); for i in 1 .. 100000 loop begin my_number := random(); /**** Здесь математические операции. ****/ exception when others then my_number := 0.0; end; end loop; t_stop := clock_timestamp(); raise notice '%s', (t_stop - t_start); end $$; Числа с плавающей точкой считаются в РАЗЫ быстрее чисел с фиксированной. И арифметика чисел с фиксированной точкой реализуется софтверно, можете в этом убедиться заглянув в исходники любой СУБД.
Извиняюсь, если кого-то задел чрезмерным квантором, я не со зла)
Давай, жги)) Даже прикольно)
Люблю такие очевидные споры, а тут еще и Си-шник) Просто пишешь функцию и используешь) const eqPrecision = 0.0000000001; function FloatEqual(AVal1,AVal2:Extended;APrec:Extended):boolean; begin result := Abs(AVal1-AVal2)<=APrec; end;
Я не утверждал, что эта проблема не решаемая, в си она решается точно так же, как и в других нативных ЯП. Но это не мешает на таких мелочах время от времени выстреливать себе в ногу даже матерым спецам.
На си это решается сравнением с константой которая представляет собой погрешность. Думаю в Паскале имеется такая же. К тому же для финансов есть Currency, который хранится в Int64, но представляется действительным
Ахренеть! Вот это лажа. Я сразу и не заметил что 4 знака только после запятой. И чо, решения из коробки нет?
https://www.freepascal.org/docs-html/rtl/sysutils/tsinglehelper.epsilon.html
забавно, это не epsilon, это денормализованное фуфло
Может объясните?
точность single примерно 7-8 знаков, поэтому eps обычно берут типа 1e-7 . минимальное нормально представимое число примерно 1e-38. 1e-45 это денормализованое fp число, которое вообще в зависимости от настроек процессора може вызватть исключение или незаметно превратиться в 0.
Будешь платить налоги, там объяснят) Сильно захотелось пошутить
Тогда вариант еще проще. Создаём константу с минимальным значением, которое вам необходимо, и сравниваем модуль разницы переменных
Речь шла о pascal/c и тд, о программах, что там неверчено в конкретной бд - я не знаю. К слову тест фигня - мерит скорость «рандома», как самой медленной операции тут.
Там коммент специально добавил: /**** Здесь математические операции. ****/ Добавляйте туда какие хотите матоперации над переменной my_number и смотрите производительность.
Ну отличный тест выходит «напиши сам», можно поверить кто-то не умеет циклом мерять производительность
Если в этом проблема, пожалуйста: do $$ declare t_start timestamp; t_stop timestamp; --my_number double precision; my_number numeric; begin t_start := clock_timestamp(); for i in 1 .. 100000 loop begin my_number := random(); my_number := (sin(my_number)*10)^5; my_number := (cos(my_number)*10)^5; my_number := (sin(my_number)*10)^5; my_number := (cos(my_number)*10)^5; exception when others then my_number := 0.0; end; end loop; t_stop := clock_timestamp(); raise notice '%s', (t_stop - t_start); end $$; do $$ declare t_start timestamp; t_stop timestamp; my_number double precision; --my_number numeric; begin t_start := clock_timestamp(); for i in 1 .. 100000 loop begin my_number := random(); my_number := (sin(my_number)*10)^5; my_number := (cos(my_number)*10)^5; my_number := (sin(my_number)*10)^5; my_number := (cos(my_number)*10)^5; exception when others then my_number := 0.0; end; end loop; t_stop := clock_timestamp(); raise notice '%s', (t_stop - t_start); end $$; Вывод: NOTICE: 00:00:02.33767s NOTICE: 00:00:00.34799s Флот отрабатывает в 5 раз быстрее!
эм, в случае numeric 64-бит число перед операцией сначала преобразовывается в плавучку, потом обратно очевидно, это медленнее, чем без преобразований
Конечно, это тоже, поэтому выше и писал, если сильная математика нужна то сначала преобразовываем потом считаем.
Можно написать без преобразований, например так: my_number := random(); for i in 1 .. 10 loop my_number := my_number * my_number; end loop; Тогда результаты вообще офигительные: NOTICE: 00:00:08.095489s NOTICE: 00:00:00.047939s
что-то тут неправильно умножение занимает пару тактов что в целых числах, что в плавучке, результаты не должны отличаться намного
Здесь нет целых чисел: https://postgrespro.ru/docs/postgresql/9.6/datatype-numeric
а, ну это постгрессовская специфика
не понял. а как же '8.1.1. Целочисленные типы'?
Уффф... На уровне процессора есть только ДВА типа чисел: целые и числа с плавающей точкой. А вещественные числа с указанной точностью которые есть практически во всех современных ЯП под капотом это массивы байт. Математические операции над ними на уровне процессора все равно сводятся к операциям над целыми числами и числами с плавающей точкой. Но все эти алгоритмы (их дохрена) работают значительно медленнее чем операции непосредственно над целыми числами (интами) и числами с плавающей запятой (флотами). Вот и все премудрости.
> А вещественные числа с указанной точностью > которые есть практически во всех современных ЯП > под капотом это массивы байт. Глобально натягиваем сову на глобус на основании одного частного случая? Берем Firebird SQL-server, (версия 3.0) и читаем документацию. Для данных с фиксированной точкой общим является форма декларации, например NUMERIC(p, s). Здесь важно понять, что в этой записи s —это масштаб, а не интуитивно предсказываемое «количество знаков после запятой». Для «визуализации» механизма хранения данных запомните для себя процедуру: • При сохранении в базу данных число умножается на 10s (10 в степени s), превращаясь в целое; • При чтении данных происходит обратное преобразование числа. Способ физического хранения данных в СУБД зависит от нескольких факторов: декларируемой точности, диалекта базы данных, типа объявления. Для третьего диалекта: +----------+-------------------+----------------+ | Точность | Тип данных | Физический тип | +----------+-------------------+----------------+ | 1-4 | NUMERIC | SMALLINT | | 1-4 | DECIMAL | INTEGER | | 5-9 | NUMERIC и DECIMAL | INTEGER | | 10-18 | NUMERIC и DECIMAL | BIGINT | +----------+-------------------+----------------+
А решение какой задачи тебе нужно? Чем currency не подходит?
Тест - полная херня. Он тут меряет количество исключений, возникающих при переполнении в зависимости от типа, а не скорость вычислений. А так же касты.
Сервер Firebird 3.0 +------------------+----+ | Тип данных | ms | +------------------+----+ | smallint | 34 | | integer | 26 | | bigint | 24 | | double precision | 24 | | numeric(18,4) | 24 | +------------------+----+ Типы данных которые занимают 64 бит, ожидаемо работают одинаково. 32 и 16 - несколько медленнее, видимо процу приходиться приводить мелкие типы в 64 бита для АЛУ.
И причем тут синусы и косинусы и подсчет денег? И я повторяюсь - речь шла о коде на паскале и тд, а не приколы с конкретными бд. Ибо как я уже писал выше - там вполне может быть не просто инт и фиксированная запятая в нем, через умножение и деление, что быстро, а что-то динамическое, что медленное.
Какой-то детский сам ей богу, не знать/отрицать фиксированную запятую на интах. Для подсчета денег синусы и тд не нужны - надо складывать, вычитать и умножать, делать - все, тут фиксированная запятая не будет «фатально медленее» флоата.
Мдя, вот засада в постгрессе.. Нет нормального типа для денег. Тот нумерик что там сделан - он для астрономических величин, и прям в доке написано что он работает намного медленнее чем все остальное. numeric переменное кол-во байт вещественное число с указанной точностью до 131072 цифр до десятичной точки и до 16383 — после а нормальные целочисленные типы там все без точки. Для нормальной работы видимо нужно врукопашную эмулировать фиксированную точку на bigint.
» Тест - полная херня. Он тут меряет количество исключений... А слабо добавить переменную-счетчик и посчитать сколько тех исключений прежде чем делать такие утверждения?
Ну сделай. Само по себе втыкание поглотителя исключений в тест - крайне странное действие. Чел не смог сделать чистый тест что бы переполнение не возникало, и на чистоту теста наплевал. Я когда свой тест писАл - столкнулся с переполнением, и переделал так что бы оно не возникало.
» Глобально натягиваем сову на глобус на основании одного частного случая? BigInteger/BigDecimal в java/C#, Decimal в Python, Big_Numbers в Ада... » При сохранении в базу данных число умножается на 10s (10 в степени s), превращаясь в целое; При чтении данных происходит обратное преобразование числа. Нууу... И остается вопрос где храним количество знаков на которые сместили.
Возможно для размеров этого типа, которые помещаются в нативный регистр ЦПУ - он делает по умному и считает их нативно? А всё что вылазит - уже с оверхедом
Всмысле? У нас переменная конкретного типа, и в ее объявлении задано, сколько цифр всего и сколько после запятой.
В доке про это не упоминается. А как по факту - ХЗ, я не работаю с PG.
» Само по себе втыкание поглотителя исключений в тест - крайне странное действие. Это странно только для людей у которых плохо с математикой. Действительно можно вывалиться из диапазона если рандом выдаст например 0.00000000000001, потом возьмем синус и возведем в надцатую степень. Вероятность такого случая мизерная, но она есть, я о таких вещах даже не задумываюсь и сразу ловлю исключения.
математикой, которая требует особо точных вычислений, занимается исчезающе малое кол-во программистов. Но если она нужна, то в любом языке (наверное) есть математические библиотеки. Конечно, можно устроить срач, вместо того, чтобы найти такую.
Да меня самого это задолбало! Вроде с числами уже вчера разобрались. Давайте переходить уже к строкам... 😊
Деление на ноль ты так же глушишь через исключения?
У меня с математикой плохо. Да, но в большинстве случаев, когда речь идет про деньги - нужна арифметика а не математика.
Обсуждают сегодня