05.04.2011, 15:46 | #1 |
NavAx
|
Еще один странный глюк
Имеем тестовый Job
X++: static void Test_Job(Args _args) { AmountCur a,b,c,d,e,f,t1,t2; ; a = 13532.5; b = 100 / 2827; c = 2776851.86; d = 2776851.86; e = a / b; f = (a / b) / c * d; e = Currency::amount(e); f = Currency::amount(f); info(strfmt("%1", e)); info(strfmt("%1", f)); } Цитата:
382 563,78
382 563,77 Дело в этом куске: X++: b = 100 / 2827; X++: b = 0.0353731871241599; Цитата:
Сообщение (15:46:14)
382 563,77 382 563,77 |
|
05.04.2011, 16:01 | #2 |
MCT
|
в 2009 такая же картина.
Мне кажется, что аксапта не хранит значения после четвертого знака после запятой.
__________________
Axapta book for developer |
|
05.04.2011, 16:15 | #3 |
MCP
|
DAX 4.0 - такого глюка нет.
Результат одинаковый. А у вас в EDT Amount свойство NoOfDecimals чему равно? Последний раз редактировалось kornix; 05.04.2011 в 16:29. |
|
05.04.2011, 16:27 | #4 |
Участник
|
А в чём вопрос? Это очевидно, что в общем случае погрешность операций с плавающей запятой не позволяет ожидать абсолютно точный результат. Например следующий код я думаю будет выдавать одинаковый результат на любой версии.
X++: info(strfmt("%1", num2str(1/3*3, 0, 16, 0, 0))); |
|
05.04.2011, 16:36 | #5 |
Участник
|
Надо смотреть не результат, а промежуточные данные
X++: static void Test_Job(Args _args) { AmountCur a,b,c,d,e,f,t1,t2; ; a = 13532.5; b = 100 / 2827; c = 2776851.86; d = 2776851.86; e = a / b; f = (a / b) / c * d; info("a / b = " + num2str(a / b,0,16,1,0)); info("(a / b) / c = " + num2str((a / b) / c,0,16,1,0)); info("(a / b) / c * d = " + num2str((a / b) / c * d,0,16,1,0)); info("round((a / b) / c * d, 0.01) = " + num2str(round((a / b) / c * d, 0.01),0,16,1,0)); } 1/3 = 0,3333333333333333 Не может иметь "бесконечное" число 3, чтобы при последующем умножении на 3 получить ровно единицу. Будет число чуть меньше единицы. На сколько "чуть" зависит от того, сколько значащих цифр хранится в данной программной оболочке Ну, и кроме того, важными являются правила округления. Система Axapta, очевидно, использует "стандартные" правила округления В приведенном изначально примере вопрос стоит об округлении числа 382563.7749999999000000 В данном контексте, фактически, с точностью до 2 знака после запятой. Очевидно, это будет число 382563.77 Все в соответствии с правилами округления. |
|
05.04.2011, 16:47 | #6 |
Модератор
|
Варианты:
1) b = 0.0353731871241599; e = 382563.7749999999;2) b = 100 / 2827; e = 382563.775;Это имхо недостаток правила. Если следовать правилу Цитата:
Округление к ближайшему целому (англ. round) — наиболее часто используемое округление. Число в десятичной системе округляют до N-ого знака в зависимости от N+1 знака:
если N+1 знак < 5, то N-ый знак сохраняют, а N+1 и все последующие обнуляют; если N+1 знак ≥ 5, то N-ый знак увеличивают на единицу, а N+1 и все последующие обнуляют. Все остальные цифры в пролете
__________________
This posting is provided "AS IS" with no warranties, and confers no rights. Последний раз редактировалось Poleax; 05.04.2011 в 17:33. Причина: N+1 |
|
05.04.2011, 17:09 | #7 |
NavAx
|
Цитата:
X++: e = a / b; |
|
05.04.2011, 17:30 | #8 |
Участник
|
А вот так
X++: f = (a / b) * d / c; Просто при вычислении выражения промежуточные итоги тоже получаеются с какой-то точностью и ошибка округления в конце концов приводит к такому результату
__________________
Axapta v.3.0 sp5 kr2 |
|
06.04.2011, 08:53 | #9 |
Злыдни
|
А что получится, если в формуле записать b = 100 / 2827.0?
__________________
люди...считают, что если техника не ломается, то ее не нужно ремонтировать. Инженеры считают, что если она не ломается, то нуждается в совершенствовании. |
|
06.04.2011, 09:46 | #10 |
Участник
|
Вот где вкралась неточность в ваших расчётах!
Переменная b, значение которой вы вычисляете как 100 / 2827 на самом деле инициализируется не 0.0353731871241599, а 0.3537318712415989e-1. это легко проверить X++: b = 100 / 2827; info(num2str(10*b, 0, 16, 0, 0)); Интересно а есть ли в аксапте возможность преобразовать вещественное значение в строку без потери значащих разрядов (т.е в экспоненциальном формате)? Последний раз редактировалось S.Kuskov; 06.04.2011 в 09:57. |
|
06.04.2011, 09:52 | #11 |
Участник
|
На самом деле, не важно, какое значение будет в b
Если переписать так X++: f = (a / b) * (d / c);
__________________
Axapta v.3.0 sp5 kr2 |
|
06.04.2011, 10:00 | #12 |
Участник
|
|
|
06.04.2011, 10:07 | #13 |
Участник
|
Это просто еще одна иллюстрация к значениям промежуточных вычислений
X++: 1 / c * d != 1 * d / c != 1 X++: 1 * (d / c) == 1 / (c / d) == 1
__________________
Axapta v.3.0 sp5 kr2 Последний раз редактировалось AndyD; 06.04.2011 в 10:09. |
|
06.04.2011, 11:12 | #14 |
NavAx
|
Можно теоретизировать что и чем инициализируется в тестовом джобе, однако в системе есть код, который приводит к расхождению на копейку сопоставленной суммы в валюте и сопоставленной суммы в основной валюте.
X++: CustVendSettle.settleNow()
{
....
paym2Invoice = 100 / custVendTransOpenDebet.settleExchRate;
....
custVendTransCredit.settleAmountCur -= Currency::amount(settleAmountCur / paym2Invoice, custVendTransCredit.currencyCode);
// так можно вылечить
custVendTransCredit.settleAmountCur -= Currency::amount((settleAmountCur / paym2Invoice) / custVendTransCredit.amountCur * custVendTransCredit.amountCur, custVendTransCredit.currencyCode);
....
settleAmountMSTCredit = Currency::amount(-(settleAmountCur / paym2Invoice) /
custVendTransCredit.amountCur * custVendTransCredit.amountMST);
custVendTransCredit.settleAmountMST += settleAmountMSTCredit;
....
} |
|
Теги |
округление |
|
Похожие темы | ||||
Тема | Ответов | |||
Глюк RunBase (AX40sp2) | 7 | |||
DeadLock. Один сеанс - несколько процессов. | 20 | |||
Глюк автоматическое рассопопоставление | 4 | |||
Enum: глюк? | 11 | |||
Help! Странный глюк.. | 8 |
|