|
Вопрос # 5 370/ вопрос открыт / |
|
Здравствуйте!
Проблема, вероятно, не из-за БД, а из-за говнокода, но всё может быть. Мне требуется сравнить 2 даты - одна из MonthCalendar, другую вытаскиваю из Access в переменную типа TDate. И на сравнение MonthCalender1.Date=AccessDate никакой реакции, хотя обе даты "29.05.2011". Только если поставить MonthCalender1.Date>=AccessDate, но это не подходит. Возиожно, дело в том, что Аксесс хранит ещё и время? Хотя это вряд ли
 |
Вопрос задал: nickel-j (статус: Посетитель)
Вопрос отправлен: 3 июня 2011, 14:04
Состояние вопроса: открыт, ответов: 2.
|
Ответ #1. Отвечает эксперт: min@y™
Всё понятно. TDate, TTime и TDateTime - это тот же Double. Сравнивать на равенство такие вещи нельзя, т.к. дробные числа хранятся в памяти в двоичном виде с погрешностью. Так что, если надо узнать, равны ли 2 даты, то:
if Trunc(Date1) = Trunc(Date2)
then ...
 |
Ответ отправил: min@y™ (статус: Доктор наук)
Время отправки: 3 июня 2011, 14:09
Оценка за ответ: 5
|
Ответ #2. Отвечает эксперт: Вадим К
Здравствуйте, nickel-j!
Посмотрите внимательней, что такое тип TDate - это обычное вещественное число. А их просто так сравнивать на равенство нельзя.
В Вашем случае наиболее правильно сделать где то так
abs(MonthCalender1.Date - AccessDate) < 0.1
вместо проверки на равенство. константу 0.1 подбираете самостоятельно, Если там будут только даты записаны, то думаю 0.1 --- 0.01 будет предостаточно. разница в 1 - это один день.
А вот Trunc, как советует min@y™, может давать неверный результат в некоторых случаях. Ведь число 5 может быть записано как 4.999999 и trunc сделает с него 4. а может быть записано в виде 5.0000001 и trunc сделает 5.
 |
Ответ отправил: Вадим К (статус: Академик)
Время отправки: 3 июня 2011, 14:14
Оценка за ответ: 5
|
Мини-форум вопроса
Всего сообщений: 34; последнее сообщение — 7 июня 2011, 21:57; участников в обсуждении: 6.
Страницы: [1] [2] [Следующая »]
|
min@y™ (статус: Доктор наук), 3 июня 2011, 14:23 [#1]:
Цитата (Вадим К):
А вот Trunc, как советует min@y™, может давать неверный результат в некоторых случаях. Ведь число 5 может быть записано как 4.999999 и trunc сделает с него 4. а может быть записано в виде 5.0000001 и trunc сделает 5.
Интересно, в каких случаях при работе с TDateTime может возникнуть такая ситуация?
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
|
|
Вадим К (статус: Академик), 3 июня 2011, 14:33 [#2]:
Может быть. Совсем на ровном месте.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
min@y™ (статус: Доктор наук), 3 июня 2011, 14:33 [#3]:
Я постоянно пользуюсь такой конструкцией при сравнении двух дат, и она меня ни разу не подводила. В качестве примера я щас написал маленькую демку:
program p5370;
{$APPTYPE CONSOLE}
uses
SysUtils;
const
dt1 = 4.999999;
dt2 = 5.000001;
dt3 = 5.000100;
begin
WriteLn(dt1: 9: 6, ' ---> ', DateTimeToStr(dt1));
WriteLn(dt2: 9: 6, ' ---> ', DateTimeToStr(dt2));
WriteLn(dt3: 9: 6, ' ---> ', DateTimeToStr(dt3));
ReadLn;
end.
Вот её консоль:
4.999999 ---> 03.01.1900 23:59:59
5.000001 ---> 04.01.1900
5.000100 ---> 04.01.1900 0:00:08
2Вадим К: Заберёшь свои слова назад, как истинный джентльмен?
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
|
|
min@y™ (статус: Доктор наук), 3 июня 2011, 14:35 [#4]:
Цитата (Вадим К):
Может быть. Совсем на ровном месте.
Приведи пример, пожалуйста.
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
|
|
nickel-j (статус: Посетитель), 3 июня 2011, 14:51 [#5]:
Спасибо! Поставил trunc - пока адекватен. Если начнёт тупить воспользуюсь abs
|
|
Вадим К (статус: Академик), 3 июня 2011, 14:53 [#6]:
А ты пару девяток добавь у меня при 4.999999999 весьма интересные результаты - "3-1-00 24:00:00"
Ошибка может возникнуть, если начнем немножко делать рассчеты с датами. То есть, посчитать разницу между двумя датами, а потом эту разницу прибавить к третьей. В этих случаях может возникать ошибка округления, которая в конце концов может привести к весьма интересным результатам.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
min@y™ (статус: Доктор наук), 3 июня 2011, 14:55 [#7]:
Цитата (nickel-j):
Поставил trunc - пока адекватен. Если начнёт тупить воспользуюсь abs
При работе с датами Trunc() тупить не начнёт. Из моего примера видно, что 4.999999 и 5.000001 - это разные даты, ведь дата - это целая часть дробного числа, как ни крути.
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
|
|
Вадим К (статус: Академик), 3 июня 2011, 14:55 [#8]:
to nickel-j: замечу - ошибка не будет возникать регулярно. Она будет возникать достаточно редко. Но будет ой как не приятно.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
min@y™ (статус: Доктор наук), 3 июня 2011, 14:56 [#9]:
Цитата (Вадим К):
А ты пару девяток добавь
Э, не, так не пойдёт! Ты сделай пример, чтобы даты были и на выходе, и на входе!
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
|
|
Вадим К (статус: Академик), 3 июня 2011, 15:27 [#10]:
Вот пример. может быть он немножко надуманный, но это как один с примеров.
program test;
uses SysUtils, DateUtils;
var
dt1, dt2, dt3, dt4:TDateTime;
i:longint;
r:double;
begin
// это типа ввод даты.
dt1 := StrToDateTime('20-1-99 00:00:00');
// скопируем
dt2 := dt1;
dt3 := dt1;
// просто найдем дату следующего
dt1:= incDay(dt1);
// а тут немножко извращенно - просто добавим посекундно
// это же тоже самое:)
for i := 1 to 60*60*24 do
dt2 := incSecond(dt2);
// dt2 и dt3 - это одно и тоже - просто на одни сутки вперед
// поэтому, добавляя вычитая одно и тоже, должны получить прежний результат
// то есть dt3 и dt4 - одно и тоже.
dt4 := dt3 - dt2 + dt1;
if trunc(dt4) = trunc(dt3) then
WriteLn('good')
else
WriteLn('bad');
writeln(dt3, ' ', dt4,' ', dt4-dt3)
end.
У меня выводит bad...
(код должен быть сильно компиляторозависимым, так что в делфи может не повториться, но может взять 2 суток или чуточку поиграться слагаемыми).
Конечно, можно заявить, что в реальной жизни такого не будет. Возможно. Но я пишу - что оно будет возникать, как только начнем манипулировать датами. Такой манипуляцией может быть сохранение данных в базу.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
min@y™ (статус: Доктор наук), 3 июня 2011, 15:47 [#11]:
Цитата (Вадим К):
Вот пример. может быть он немножко надуманный
Это перебор. Нахрена IncSecond(), когда идёт работа с датами? Естественно, в цикле набежит погрешность, ещё бы, 86400 раз делать операцию с плавающей точкой.
Пусть ошибка возникает у тех, кто будет писать программы таким образом. А у нормальных людей ошибка не вылезет.
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
|
|
Вадим К (статус: Академик), 3 июня 2011, 16:00 [#12]:
я не писал, что ошибка будет возникать всегда. Она будет возникать редко, но ой как будет неприятно.
В данном случае я специально сделал много выражений, что бы ошибка вылазила гарантировано.
Нормальные люди знают, что такое вещественные числа, и как их правильно сравнивать.
Галочка "подтверждения прочтения" - вселенское зло.
|
|
nickel-j (статус: Посетитель), 3 июня 2011, 17:24 [#13]:
Вообще-то Вадим прав - по транку равенство выдает 30.05.2011 и 2.06.2011, а реальные равенства типа 30 и 30 не выдает вообще. Т.е. стабильность никакущая. С другой стороны модуль тоже не радует. Странно, кажется - простенькая задачка - а сколько заморочек
|
|
min@y™ (статус: Доктор наук), 3 июня 2011, 17:29 [#14]:
Цитата (nickel-j):
транку равенство выдает 30.05.2011 и 2.06.2011
Такого быть не может. Показывай код.
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
|
|
Вадим К (статус: Академик), 3 июня 2011, 18:11 [#15]:
в модуле DateUtils есть функция function CompareDate(const A, B: TDateTime): TValueRelationship; которая умеет сравнивать две даты. Возвращает 0, если даты равны и -1 и +1 в других случаях.
Также есть функция function SameDate(const A: TDateTime; const B: TDateTime):Boolean;
которая как раз делает то, что нужно.
Попробуйте на ваших примерах
Галочка "подтверждения прочтения" - вселенское зло.
|
|
nickel-j (статус: Посетитель), 3 июня 2011, 18:45 [#16]:
Ага, там же я нашел DateOf - но SameDate конечно поэстетичней.
Код приблизительно такой - суть в начислении % по вкладам, событие идёт по таймеру, увеличивающему дату на 1 день.
if SameDate(MonthCalendar1.Date,inv[j].getdate)=true then
begin
if inv[j].Cap=true then
begin
с капитализацией
end
else //без капитализации
begin
end
ADOQuery1.SQL.Text:='update Вклад set
Сумма='+inttostr(profit)+',Датаначисления="'+datetostr(incmonth(MonthCalendar1.Date))+'" where счёт='+inv[j].contract;
ADOQuery1.ExecSQL;
Memo1.Lines.Add(datetostr(MonthCalendar1.date)+'счёт '+inv[j].contract+' сумма = '+inttostr(profit));
end
И иногда выводится начисление на одну дату, а затем еще через день - и потом уже дата начисления уходит на 2 месяца вперед. Скорее всего это не из-за Trunc - что бы не подставлял - порой возникает.
|
|
min@y™ (статус: Доктор наук), 3 июня 2011, 19:00 [#17]:
Цитата (nickel-j):
Код приблизительно такой
Покажи, как с Trunc() было!
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
|
|
nickel-j (статус: Посетитель), 3 июня 2011, 19:25 [#18]:
с Trunc() вместо if SameDate(MonthCalendar1.Date,inv[j].getdate)=true then было if Trunc(MonthCalendar1.Date)=Trunc(inv[j].getdate) then
|
|
min@y™ (статус: Доктор наук), 3 июня 2011, 19:32 [#19]:
Понятно. Посмотрел бы в отладчике, чему равно то и другое. Для начала.
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
|
|
bugmenot (статус: 3-ий класс), 3 июня 2011, 19:40 [#20]:
Цитата (Вадим К):
abs(MonthCalender1.Date - AccessDate) < 0.1
вместо проверки на равенство. константу 0.1 подбираете
Грубо, см. Math.SameValue для оценки бесконечно малых
Цитата (nickel-j):
if SameDate(MonthCalendar1.Date,inv[j].getdate)=true then
if 2 * 2 = 4 = True <> False then Beep();
виконання програми розпочинається з того самого мiсця, де призупинилося.
|
Страницы: [1] [2] [Следующая »]
Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.
|