Экспертная система Delphi.int.ru

Сообщество программистов
Общение, помощь, обмен опытом

Логин:
Пароль:
Регистрация | Забыли пароль?

Delphi.int.ru Expert

Другие разделы портала

Переход к вопросу:

#   

Статистика за сегодня:  


Лучшие эксперты

Подробнее »



Вопрос # 5 370

/ вопрос открыт /

Здравствуйте!
Проблема, вероятно, не из-за БД, а из-за говнокода, но всё может быть. Мне требуется сравнить 2 даты - одна из MonthCalendar, другую вытаскиваю из Access в переменную типа TDate. И на сравнение MonthCalender1.Date=AccessDate никакой реакции, хотя обе даты "29.05.2011". Только если поставить MonthCalender1.Date>=AccessDate, но это не подходит. Возиожно, дело в том, что Аксесс хранит ещё и время? Хотя это вряд ли

nickel-j Вопрос ожидает решения (принимаются ответы, доступен мини-форум)

Вопрос задал: 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™

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™

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™

min@y™ (статус: Доктор наук), 3 июня 2011, 14:35 [#4]:

Цитата (Вадим К):

Может быть. Совсем на ровном месте.

Приведи пример, пожалуйста.
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
nickel-j

nickel-j (статус: Посетитель), 3 июня 2011, 14:51 [#5]:

Спасибо! Поставил trunc - пока адекватен. Если начнёт тупить воспользуюсь abs
Вадим К

Вадим К (статус: Академик), 3 июня 2011, 14:53 [#6]:

А ты пару девяток добавь:) у меня при 4.999999999 весьма интересные результаты:) - "3-1-00 24:00:00"
Ошибка может возникнуть, если начнем немножко делать рассчеты с датами. То есть, посчитать разницу между двумя датами, а потом эту разницу прибавить к третьей. В этих случаях может возникать ошибка округления, которая в конце концов может привести к весьма интересным результатам.
Галочка "подтверждения прочтения" - вселенское зло.
min@y™

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™

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™

min@y™ (статус: Доктор наук), 3 июня 2011, 15:47 [#11]:

Цитата (Вадим К):

Вот пример. может быть он немножко надуманный

Это перебор. Нахрена IncSecond(), когда идёт работа с датами? Естественно, в цикле набежит погрешность, ещё бы, 86400 раз делать операцию с плавающей точкой.
Пусть ошибка возникает у тех, кто будет писать программы таким образом. А у нормальных людей ошибка не вылезет.
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
Вадим К

Вадим К (статус: Академик), 3 июня 2011, 16:00 [#12]:

я не писал, что ошибка будет возникать всегда. Она будет возникать редко, но ой как будет неприятно.
В данном случае я специально сделал много выражений, что бы ошибка вылазила гарантировано.
Нормальные люди знают, что такое вещественные числа, и как их правильно сравнивать.
Галочка "подтверждения прочтения" - вселенское зло.
nickel-j

nickel-j (статус: Посетитель), 3 июня 2011, 17:24 [#13]:

Вообще-то Вадим прав - по транку равенство выдает 30.05.2011 и 2.06.2011, а реальные равенства типа 30 и 30 не выдает вообще. Т.е. стабильность никакущая. С другой стороны модуль тоже не радует. Странно, кажется - простенькая задачка - а сколько заморочек
min@y™

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

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™

min@y™ (статус: Доктор наук), 3 июня 2011, 19:00 [#17]:

Цитата (nickel-j):

Код приблизительно такой

Покажи, как с Trunc() было!
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
nickel-j

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™

min@y™ (статус: Доктор наук), 3 июня 2011, 19:32 [#19]:

Понятно. Посмотрел бы в отладчике, чему равно то и другое. Для начала.
Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп!
bugmenot

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] [Следующая »]

Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.

Версия движка: 2.6+ (26.01.2011)
Текущее время: 22 февраля 2025, 11:31
Выполнено за 0.04 сек.