Текущий архив: 2004.03.14;
Скачать: CL | DM;
ВнизПроблемы и приколы с действительными числами Найти похожие ветки
← →
McSimm (2004-02-19 13:50) [0]Просто утомили.
Почти два дня не могли нормально огруглить значения из БД.
Танцы с бубном включали в себя все па с разнообразными типами данных, использованием разных наборов и последовательностей вызовов процедур.
Проблема началась с того, что некий расчет, осуществляемый в программе приводит к погрешности в 2 копейки.
Задача сначала показалась простой. Но...
На результат влияло все. Причем выполнение того же самого кода несколько раз приводит к разным результатам. Всего тут не опишешь.
Вот, хочу поделиться интересным наблюдением. Очень устойчивый эффект.
Есть минимальное приложение, в котором реализован коннект к БД (MS-SQL) и простой запрос.
Данные запроса выводятся корректно (внимание!) до первого нажатия какой-нибудь кнопки на клавиатуре в этом приложении. Не важно на каком контроле.
К примеру, до первого нажатия кнопок я могу сколь угодно долго перемещаться (мышью по гриду или программно) по записям НД и получаю след рез-т
1,275000000000000000
1,225000000000000000
Но если нажать любую клавишу (АЦ, управляющую - неважно), после этого знаменательного события тот же НД постоянно выглядит
1,274999999999999840
1,225000000000000160
И так будет до закрытия приложения. Переконнект к базе не поможет.
Прикол с клавиатурой это только один из числа интересных моментов. Например
function CCC(AValue: Extended): string;
и
function CCC(const AValue: Extended): string;
давали разный результат... некоторое время, потом перестали :)))
И т.п.
← →
ALEIIIKA (2004-02-19 13:54) [1]Это не ответ, просто посмотрел анкету :-) (хороший прикол)
← →
Romkin (2004-02-19 13:57) [2]Ужас! Удивительно, как сайт до сих пор работает ;)
← →
McSimm (2004-02-19 14:01) [3]Я собственно и не жду ответа, поскольку вопроса не формулировал.
Просто делюсь впечатлениями, может кто-то свои впечатления или соображения на эту тему предложит.
При всем желании не могу сформулировать нормальный вопрос. Вариант "Люди, помогите правильно округлить, а то задолбался уже" не годится :)
Собственно проблема уже решена, правда некорректно, полушаманским методом. Ну, какие проблемы, такие и методы :)
← →
McSimm (2004-02-19 14:02) [4]>Romkin © (19.02.04 13:57)
Ну, так и работает. Разве не заметно :)))
← →
ALEIIIKA (2004-02-19 14:02) [5]Важно не чтобы программа работала правильно, главное чтобы она выдавала правильный ответ
← →
McSimm (2004-02-19 14:06) [6]
> ALEIIIKA © (19.02.04 14:02) [5]
> Важно не чтобы программа работала правильно, главное чтобы
> она выдавала правильный ответ
Половину времени я учился программировать, вторую половину отучивался программировать неправильно.
Не всегда получается, конечно :)
← →
alxx (2004-02-19 14:08) [7]Если использовать float, а не decimal или numeric, то эти приколы будут часто.
float - используется для "приближенных чисел".
← →
Fredericco (2004-02-19 14:08) [8]2 McSimm ©
Таже феня была, мне тока помог Currency в Дельфе и хранение числа в БД как строки. Слава БГ мне не приходилось сортировать в БД по цене.
Криво, конечно, но умнее не придумал. Каюсь LMD.
← →
DiamondShark (2004-02-19 14:10) [9]Приложение и сервер на одном компе?
Кто считает результат? Приложение или сервер?
← →
Тимохов (2004-02-19 14:14) [10]Когда задолбала похожая на вашу ситуация перешли на другой тип: decimal(28,10) форева!
← →
McSimm (2004-02-19 14:15) [11]>Если использовать float,
Это понятно.
> Приложение и сервер на одном компе?
Разными способами, на разных компах.
>Кто считает результат? Приложение или сервер?
Результат необходимо считать в программе. Конечная задача (отчет) не годится для реализации на сервере. Серверные расчеты в таких безобразиях пока замечены не были.
← →
Игорь Шевченко (2004-02-19 14:18) [12]McSimm © (19.02.04 14:15)
Статья Антона Григорьева на "Королевстве" не поможет ?
← →
Vuk (2004-02-19 14:24) [13]Как сделано у нас:
1. Все данные, касающиеся денег хранятся в MS-SQL в виде numeric(18,2)
2. В клиенте для представления полей используется тип TBCDField.
← →
DiamondShark (2004-02-19 14:31) [14]А как, если не секрет, была проблема решена?
Первое, что на ум приходит, перед вычислениями выставлять нужное значение в CW FPU.
← →
jack128 (2004-02-19 14:32) [15]
> >Если использовать float,
> Это понятно.
Вообще и для Double результат нормальный. Погрешность после 17 знака. Так что я не поймы в чем проблема? Хотя если считать миллионы с точностью до копейки, то проблемы есть, конечно, ну так для этого нумерики и придуманы..
← →
Nikolay M. (2004-02-19 14:38) [16]7 чудес и 2 фокуса на Дельфи не смотрел, может на мысль натолкнет?
http://www.citforum.ru/programming/delphi/miracles/
← →
McSimm (2004-02-19 14:44) [17]>Игорь Шевченко © (19.02.04 14:18)
>Статья Антона Григорьева на "Королевстве" не поможет ?
Нет, статья хорошая, конечно, но все это я давно знаю.
> Vuk © (19.02.04 14:24) [13]
> Как сделано у нас:
Все почти аналогично. Кроме numeric(18,3), так надо.
> Так что я не поймы в чем проблема?
Проблема в том, что результат просчитанный на калькуляторе, в Excell, в уме, на сервере средствами SQL отличается от результата вычислений на клиенте. Требуемая точность - одна копейка.
> А как, если не секрет, была проблема решена?
Немного изменили функцию округления. Добавили 1 в 4м разряде - все погрешности покрылись этой поправкой. Криво, но времени на корректное решение просто уже нет.
← →
Romkin (2004-02-19 14:46) [18]Для того, чтобы решить, надо знать, что и как считается. Куча литературы, тот же Кнут.
Я в свое время хорошую школу прошел с мат. методами. Взять, к примеру, почему рекомендуют использовать LUP-разложение вместо метода Гаусса...
Бухгалтерия - спросите бухгалтера :) Хорошего. ТАм все суммы с двумя знаками, хранятся точно. Для устранения погрешностей - все продумано. Правда, иногда приходится хранить 4 знака, например, в курсе валюты.
Совет - double годится для 98% расчетов, extended - не надо. Ну и currency, где нужно
← →
Игорь Шевченко (2004-02-19 14:52) [19]А мы вот так округляем:
function HSRoundToEx(const R: Extended; const Places: TRoundToRange): Extended;
const
Correction = 1.0E-17;
var
y,f,LFactor: Extended;
begin
LFactor := IntPower(10, Places);
y := Abs(R) * LFactor;
f := Frac(y);
if (f > 0.5 - Correction) and (f < 0.5 + Correction) then
y := Int(y) + (Trunc(y) mod 2)
else if f > 0.5 + Correction then
y := Int(y) + 1.0
else
y := Int(y);
Result := y / LFactor;
if R < 0 then
Result := -Result;
end;
function HSRoundTo(const R: Double; const Places: TRoundToRange): Double;
const
Correction = 1.0E-13;
var
y, f, LFactor: Double;
begin
LFactor := IntPower(10, Places);
y := Abs(R) * LFactor;
f := Frac(y);
if (f > 0.5 - Correction) and (f < 0.5 + Correction) then
y := Int(y) + (Trunc(y) mod 2)
else if f > 0.5 + Correction then
y := Int(y) + 1.0
else
y := Int(y);
Result := y / LFactor;
if R < 0 then
Result := -Result;
end;
← →
Vuk (2004-02-19 14:53) [20]to McSimm:
Библиотека для доступа к MS-SQL какая? У нас SDAC.
← →
McSimm (2004-02-19 14:53) [21]
> Romkin © (19.02.04 14:46) [18]
Это все хорошо, пока все хорошо.
А что делать, если введенное число 1,275 с клавиатуры несколько раз в БД в одном случае округляется как 1,27 а в другом как 1,28. Одним и тем же алгоритмом. Иногда даже из одной и той же записи в НД.
>Nikolay M. © (19.02.04 14:38)
Конечно нет. Эти трюки довольно примитивные.
← →
Игорь Шевченко (2004-02-19 14:57) [22]Игорь Шевченко © (19.02.04 14:52)
Забыл добавить: portions (c) Radionov Alexey ;)
← →
Romkin (2004-02-19 14:59) [23]Что делать? Надо определить, как надо, чтобы округлялось :))
ТАк и округлять
← →
McSimm (2004-02-19 15:00) [24]>Vuk © (19.02.04 14:53)
>Библиотека для доступа к MS-SQL какая? У нас SDAC.
MDAC, вообще-то...
А при чем тут клавиатура? ;)
← →
Vuk (2004-02-19 15:04) [25]to McSimm:
>А при чем тут клавиатура? ;)
Вот и я про то, что вроде бы должна быть ни при чем. Но с другой стороны у нас со SDAC таких глюков не было. :o)
← →
Petr V. Abramov (2004-02-19 15:04) [26]Я много лет назад забил проводить какие-нить расчеты на клиенте. Благо, пользовался Oracle, там оно и проще, и без приколов. А Delphi - классный инструмент. Для рисования окошек. Может, оно, конечно, и лечится, но человек я ленивый, поэтому см. начало поста.
← →
McSimm (2004-02-19 15:07) [27]>Vuk © (19.02.04 15:04)
Собственно я согласен, просто немного сыронизировал.
Проблема конечно не в Делфи и не в алгоритмах округления, а в том, что данные из НД поступают не всегда корректно. И тут библиотека вполне может сыграть роль
← →
DiamondShark (2004-02-19 15:10) [28]
> А при чем тут клавиатура? ;)
Кто-то меняет FPU CW.
Где-то в недрах обработки клавиатуры.
← →
McSimm (2004-02-19 15:19) [29]
> Кто-то меняет FPU CW.
> Где-то в недрах обработки клавиатуры.
Угу, так и есть :)
При запуске приложения FNSTCW возвращает $1372
После клавиатурного события - $1272
← →
Vuk (2004-02-19 15:22) [30]Вот шабака! Интересно, и кто есть сей злобный зверь?
← →
McSimm (2004-02-19 15:34) [31]Да, ответ как всегда был на поверхности. Изменяется (какой-то шабакой) бит, отвечающий за точность.
Боюсь, чтобы найти чьими стараниями это происходит, копать глубоко придется. Времени нет, но интересно.
← →
REA (2004-02-19 16:33) [32]Сдается мне все же для таких вычислений есть типы Currency, Money там всякие и т.п.
← →
REA (2004-02-19 16:35) [33]Хотя первый раз слышу, что бы клавиатура меняла FPUCW. Ладно там OpenGL какой-нить.
← →
han_malign (2004-02-19 16:38) [34]да действительно интересно:
function TCommonDialog.TaskModalDialog(DialogFunc: Pointer; var DialogData): Bool;
type
TDialogFunc = function(var DialogData): Bool stdcall;
var
ActiveWindow: HWnd;
WindowList: Pointer;
FPUControlWord: Word;
FocusState: TFocusState;
begin
ActiveWindow := GetActiveWindow;
WindowList := DisableTaskWindows(0);
FocusState := SaveFocusState;
try
Application.HookMainWindow(MessageHook);
asm
// Avoid FPU control word change in NETRAP.dll, NETAPI32.dll, etc
FNSTCW FPUControlWord
end;
try
CreationControl := Self;
Result := TDialogFunc(DialogFunc)(DialogData);
finally
asm
FNCLEX
FLDCW FPUControlWord
end;
Application.UnhookMainWindow(MessageHook);
end;
finally
EnableTaskWindows(WindowList);
SetActiveWindow(ActiveWindow);
RestoreFocusState(FocusState);
end;
end;
← →
Piter (2004-02-19 18:48) [35]почему рекомендуют использовать LUP-разложение вместо метода Гаусса
Да это же просто. Чтобы если есть уравнение с рахными правыми частями не пересчитывать.
Хотя я про LU разложение. А что такое LUP?
← →
Anatoly Podgoretsky (2004-02-19 19:15) [36]McSimm © (19.02.04 15:34) [31]
Это модет быть в АПИ, были жалобы на это.
Страницы: 1 вся ветка
Текущий архив: 2004.03.14;
Скачать: CL | DM;
Память: 0.53 MB
Время: 0.012 c