Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 2004.03.14;
Скачать: [xml.tar.bz2];

Вниз

Проблемы и приколы с действительными числами   Найти похожие ветки 

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.013 c
8-43702
Klon
2003-11-14 12:27
2004.03.14
Разбиение многоугольников


3-43332
Oksana
2004-02-17 10:20
2004.03.14
Запрет на добавление записей в DBGrid.


14-43830
Chapha
2004-02-21 22:22
2004.03.14
Игры


1-43518
rezak
2004-02-28 18:35
2004.03.14
вопрос с лабелом


1-43433
Serginio666
2004-02-27 13:56
2004.03.14
Implements в Delphi 8





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский