powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / и снова копейки: #define, С++ , Builder 2009 ?
21 сообщений из 46, страница 2 из 2
и снова копейки: #define, С++ , Builder 2009 ?
    #39354778
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98Потому что хвост вытащил - нос увяз((
потому как заранее неизвестно, точное ли оно или с погрешностью вычислений
А ты посчитай. У double матрисса 52 бита (точнее почти 53) 2^52 ~=10^15, т.е. первые 15 десятичных разрядов будут представлены точно и плюс хвостик погрешности, т.е. погрешность вылезет на 16-м разряде.
Если у тебя рубли в копейками, то максимум 10 000 000 000 000.00 можно точно представить.
Используем один разряд для разграничения, остается 1 000 000 000 000.00. До этой суммы можешь не переживать за погрешности.
Но погрешность надо учитывать, т.к. она в обе стороны может исказить точное значение, поэтому 0.5 надо представлять как 0.50 - 0.001, т.е. 0.499
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39354852
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ИзопропилMasterZivнет, арифметика тоже неточная.
что значит "неточная"?
десятичная - тож окажется "неточной": 10/3 - и приехали


10/3 - не десятичная дробь.

арифметика с плач. точкой неточная, потому что представление чисел уже неточно, все числа изображаются в виде чисел, ....


блин, да лень писать, неточная арифметика с плавающей точкой, и все. хочешь доказательств - найди сам в сети.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39354872
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MasterZiv,

спорить бесполезно - называйте неточной
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355151
m_Sla
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98,

в mingw одинаковые результаты
автор5665.455 = 5665.45499999999992724042
5665.45 = 5665.44999999999981810106
5665.46 = 5665.46000000000003637979

1 = 566546.00000000000000000000
2 = 566546.00000000000000000000
3 = 5665.46000000000003637979
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
#define TO_RUB(A) (((A)-floor(A))>=0.5?ceil(A):floor(A))
#define TO_KOP(A) (TO_RUB((A)*100.0f)/100.0f)

double NDS (int k);

using namespace std;

//-------------------------------------------------------

int main()
{
    printf("5665.455 = %20.20f\n", 5665.455);
    printf("5665.45  = %20.20f\n", 5665.45);
    printf("5665.46  = %20.20f\n", 5665.46);
    printf("\n");

    NDS(0);

    return 0;
}
//-------------------------------------------------------

double NDS ( int k)
{
        double nds = 0.0;
        double xx, yy= 5665.455;

        {  //// 1) ответ 5665,45
            xx= yy;
            nds = TO_KOP( nds + TO_RUB( xx *100.0f) /100.0f);
            nds= TO_RUB( xx*100.0f );

            printf("1 = %20.20f\n", nds);
        }

        {  //// 2) ответ 5665,46
            xx= yy *100.0f;
            nds = TO_KOP( nds + TO_RUB( xx ) /100.0f);
            nds= TO_RUB( xx );

            printf("2 = %20.20f\n", nds);
        }

        {  //// 3)   ответ 5665,45
            xx= yy;
            nds = TO_KOP( nds + TO_KOP( xx ));
            nds= TO_KOP( xx );

            printf("3 = %20.20f\n", nds);
        }//end_if
    return nds;
}

только числа 5665.45 и 5665.46 в любом случае в double получаются приближенными, имхо округлять до 0,01 их бесполезно
и выкинь в округлении floor и ceil, возможно у билдера в них ошибки, замени на
Код: plaintext
1.
2.
double TO_RUB(double A) { return (int)( A + 0.5 ); }
double TO_KOP(double A) { return (int)( A*100.0 + 0.5)/100.0; }
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355597
Пётр Седов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98, может для денежных расчётов использовать рациональные числа? Ведь арифметические операции с ними делаются без погрешности. Например, есть библиотека GMP :
GMP function categories
...
2. High-level rational arithmetic functions (mpq). This category consists of about 35 functions, but all mpz functions can be used too, by applying them to the numerator and denominator separately.
...
4. C++ class based interface to all of the above. (The C functions and types can of course be used directly from C++ too.)

Изопропилспорить бесполезно - называйте неточнойFloating point арифметика -- детерминированная (без random-а), но неточная:
Код: plaintext
1.
2.
double x = 10000000000000000.0;
assert(x + 1.0 == x);
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355617
Фотография Изопропил
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Пётр СедовFloating point арифметика -- детерминированная (без random-а), но неточная
что характерно - арифметика с фиксированной точкой обладает этими же свойствами
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355659
Пётр Седов
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ИзопропилПётр СедовFloating point арифметика -- детерминированная (без random-а), но неточная
что характерно - арифметика с фиксированной точкой обладает этими же свойствамиПоэтому fixed point арифметику тоже лучше не использовать для денежных расчётов. А вот рациональная арифметика -- она без погрешности, например с помощью библиотеки GMP:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
#include <stdio.h>
#include "gmpxx.h"

int main() {
  mpq_class a("3/10", /*base:*/10); // в точности 0.3
  mpq_class b("1/10", /*base:*/10); // в точности 0.1
  mpq_class c = a + b;
  printf("c = %s\n", c.get_str().c_str());

  mpq_class x("10000000000000000", /*base:*/10);
  mpq_class y("1", /*base:*/10);
  mpq_class z = x + y;
  printf("z = %s\n", z.get_str().c_str());

  return 0;
}

Вывод на консоль:
Код: sql
1.
2.
c = 2/5
z = 10000000000000001

Но это конечно медленнее работает, чем с double-ами.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355744
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Телушка - полушка, да рубль перевоз. ТЗ такое было изначально, позволяющее вольности. Кабы с нуля всё строить, а тут тогда всё перелопачивать. Да и инткрфейс тоже тогда будет (уже) тормозить, ну то есть всю концепцию заново тогда во всх проектах и процы в базе тоже. И ради чего? чтоб в договоре и связанных с ним документах всегда было тип-топ.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355763
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima T1 000 000 000 000.00. До этой суммы можешь не переживать за погрешности.
Ты забываешь, что 15 знаков вмсте с дробью, т.е. вылезет и на 123456.7499999988. Просто чем точнее отсекать (0.000001), тем меньше будет вероятность ошибок 1-го и 2-го рода - я об этом сейчас думаю. Всё равно неск. раз в году происходит. Только сейчас первый случай, в к-ром билдер замечен, а причина до сих пор непонятна.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355771
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98Dima T1 000 000 000 000.00. До этой суммы можешь не переживать за погрешности.
Ты забываешь, что 15 знаков вмсте с дробью, т.е. вылезет и на 123456.7499999988. Просто чем точнее отсекать (0.000001), тем меньше будет вероятность ошибок 1-го и 2-го рода - я об этом сейчас думаю. Всё равно неск. раз в году происходит. Только сейчас первый случай, в к-ром билдер замечен, а причина до сих пор непонятна.
Я не забываю. То что после третьего знака после запятой - неважно. Зачем тебе точнее если ты в копейках учет ведешь? Т.е. считаешь без округлений и в конце округляешь.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355839
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну да, слагаемые должны быть большей точности, чем сумма. Но в данной ветке числа не вычисляются, а много раз складываются готовые НДС из БД, в данном случае ~7-8 раз в двойном цикле. И предполагается, что их сумма и каждый промежуточный рез-т в конце сойдётся с итогом НДС. А числа гадкие, и на экране сразу видно навроде
ИТОГО=ххх,00
НДС= уу,99
ВСЕГО с НДС= zzz,00

Операция round(a+b+c+d ) не дистрибутивна относительно суммы, и для суммы НДС округлять нужно бы каждое слагаемое. Ну это как "немного изменить последнюю цифру телефонного номера".
При том ведь ошибка вылезла на другом значении, потому что слагаемые не округлялись - я не долго думая округлил, решил посмотреть как скажется в других случаях, ткнул в случайное место - а тут такая фигня. Ну то есть хвост вытащил - нос увяз.

Всегда сконялся к варианту двойного преобразования по типу:
(double)( (__int64) (aa+0.5) + bb ) , но любая правка кроме подмены макроса - это в сотнях мест, и опять поедут старые значения, а вообще тысяча мест, где подобные сложения. Ну неподъёмно это, поэтому я так консервативен в ответах .
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355844
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
m_Sladouble TO_RUB(double A) { return (int)( A + 0.5 ); }Наверное это лучший вариант.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355860
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98m_Sladouble TO_RUB(double A) { return (int)( A + 0.5 ); }Наверное это лучший вариант.
Ну и чем он лучший? Разве что побыстрее твоего отработает. Это математическое округление через округление вниз. 1.234999999999999999 округлится до 1.23.
Проблемы с точностью тут абсолютно те же что и твоем первом посте.

Ты пойми, погрешность она в обе стороны бывает: и в плюс и в минус. У тебя все проблемы в том что в минус ты не учитываешь.
В данном случае надо A + 0.5 1
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355864
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98Ну да, слагаемые должны быть большей точности, чем сумма. Но в данной ветке числа не вычисляются, а много раз складываются готовые НДС из БД, в данном случае ~7-8 раз в двойном цикле. И предполагается, что их сумма и каждый промежуточный рез-т в конце сойдётся с итогом НДС. А числа гадкие, и на экране сразу видно навроде
ИТОГО=ххх,00
НДС= уу,99
ВСЕГО с НДС= zzz,00
Если готовые, посчитанные с точностью до копейки, т.е. округленные до копеек при сохранении, то в худшем случае погрешность будет 8/2^53 = 8.8*10^-16, т.е. гарантированно 15 первых знаков точно будут вычислены, дальше надо просто округлить результат до копеек, чтобы окончательно убрать накопленную погрешность.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355937
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dima_T, да, он быстрее в работе и для исправления. В БД всякие значения, рассчитанные в разное время в прошлом: с ошибками и без, с округлениями и без, исправленные и нет. Для некоторых денег это неважно, для других важно, но не фатально.

Повторюсь exp98об этом я в курсе ещё с больших машин ....
Переделывать тогда уж сотню файлов и процы в БД, да и саму БД переводить на копейки. Это не вариант.
Вопрос в том, почему дефайн считает не как я думаю, ну или наоборот.Последнее изначально было единственной целью вопроса.

Ну вот ответь мне, какое множество ошибок ты предлагаешь упорядочить 1-го рода, 2-го рода? или некую их взвеш. сумму и тогда в каком соотношении? ну чтоб хотя бы численно ргументировать ...
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39355976
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98Ну вот ответь мне, какое множество ошибок ты предлагаешь упорядочить 1-го рода, 2-го рода? или некую их взвеш. сумму и тогда в каком соотношении? ну чтоб хотя бы численно ргументировать ...
Что такое "род" я не понял.

Суть в следующем: на вход должны поступать значения с минимальной погрешностью, в твоем случае округленные до копеек, затем производиться вычислительные операции и по окончанию результат округляться до копеек.

Если вход заведомо округлен (или округлить принудительно), то можно рассчитывать максимальную накопленную погрешность. Исходная погрешность составляет плюс-минус X*1/2^53, где X точное значение. Потому что дискретность мантиссы 1/2^52 и при преобразовании из десятичного приводится к наиболее близкому большему или меньшему, т.е. погрешность это плюс-минус половина дискретности.
Дальше просто: делаем предположение что все входные данные это наихудший случай, например надо сложить 8 одинаковых значений с максимальной погрешностью, т.е. погрешность суммы будет 8*X*1/2^53 = X*8.8*10^-16.
Теперь ищем максимальный результат, в котором будет искажение более чем на 0.005, т.е. округление до копеек даст ошибку.
0.005 / 8.8*10^-16 = 5 681 818 181 818,18
Если у тебя результаты не превышают 5 681 818 181 818,18 то результат сложения 8 значений будет гарантированно точный.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39356009
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
По поводу округления (понял как точно посчитать), если смещаем на 0,01 копейки (+0.0051), чтобы отрицательные погрешности учесть, то можно смело так округлять числа до
Код: plaintext
0.0001 / 2^53 = 900 719 925 474,10
или почти 15 десятичных знаков.

С математикой округления можно еще пооптимизировать, сходу не соображу до скольки можно добавить, вроде 0.0004 (+0.0054) должно тоже точно работать и увеличит макс. значение до 3 602 879 701 896,40 или 15 десятичных знаков. Подзабыл математику :(
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39356024
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Это из статистики о принятии и отпрасывании гипотез.
В одном случае мы (ошибочно) принимаем некоторые события за истинные. В другом случае (ошибочно) отбрасываем истинные события.

У нас есть двоичное число. Используем его в плавающем формате для сравнения. В проге при сравнении мы отождествляем все числа, отличающиеся от некоторого на маленькую дельту. При этом некоторые числа справедливо будут отождествлены, а некоторые - в рез-те погрешностей вычислений.
То есть часть чисел будет представлено округлённым справедливо, а часть нет, но нам приходится принимать это как есть. Гипотеза (о справедливости округления) содержит ошибки "одного рода".

Остальные числа не пройдут сравнение и не будут округлены аналогично. Соответственно парная первой ггипотеза о ненужности округления этих чисел содержит ошибку "другого рода".

Ну и вопрос мой был каких ошибок больше и на сколько: справедливо округлённых или несправедливо неокруглённых, примерно так.

А вообще мне сейчас с базой и VS надо копаться, вникать в ответы позднее буду.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39387910
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Нконец вернулся к этому вопросу. Вот ты писал
Dima Tесли смещаем на 0,01 копейки (+0.0051) .... до скольки можно добавить, вроде 0.0004 (+0.0054) должно тоже точно работать ... В целом правильные рассуждения, только ошибок принятия решиний всё равно не избежать. Я сегодня смотрел на вполне безобидных числах.
Например получил я в расчёте с ТОЧНЫМИ числами, нечто, что выглядит как yy= 5665.4549
Пусть именно оно и есть НДС.

Если использовать логику окруления +-0.0051 руб, то НДС= 5665.45,
если +-0.0052, то НДС= 5665.46

Например, (уу*100.0)*0.01 = 5665.454899999999

А как д.б. знает только калькулятор или же спецалгоритмы .... только ГУИ тормозить будет при обсчёте и смене массива данных. И использовать это только для денег. Сейчас TO_RUB() используется иногда и для обчной целой части.

И легко напридумать похожих чисел для порогов вида 0.0050001 руб и т.п.
Я смотрел в варианте, т.к. это самая простая правка
Код: plaintext
1.
2.
3.
4.
5.
#define MY_EPSILON_0_5  0.51
double TO_KOP(double A) { return (__int64)( A*100.0 + MY_EPSILON_0_5) *0.01; }

nds= TO_KOP( yy);
printf("%20.20f\n", nds);


Моё резюме таково же как выше все и пишут, double - не самый лучший формат для денежных операций.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39387925
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98, недавно похожая тема была 20077393 , подходит double для денег, но пользоваться им надо аккуратно.

Что касается НДС, то там где деление (умножение на нецелое), точность в принципе теряется.
Сумма НДС 18%Сумма с НДС1.240.221.461.240.221.461.240.221.46

Итого по столбцам
Сумма НДС 18%Сумма с НДС3.720.664.36
Но 18% от 3.72 это 0.67 будет.
...
Рейтинг: 0 / 0
и снова копейки: #define, С++ , Builder 2009 ?
    #39388386
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну разумеется аккуратно. Я за себя говорю, в моём случае тогда придётся затормозить ГУИ или же частично переделать фреймворк в денежной части под эффективные алгоритмы, возможно с правками процедур в БД. А параллельно с этим продолжать доработки на прежней схеме. Не до такой же степени я трудоголик. И всем спасибо за варианты и ссылки.
...
Рейтинг: 0 / 0
21 сообщений из 46, страница 2 из 2
Форумы / C++ [игнор отключен] [закрыт для гостей] / и снова копейки: #define, С++ , Builder 2009 ?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]