Какая прелесть! или Еще немного о теории относительности.

Модераторы: Duncon, Naeel Maqsudov, Игорь Акопян, Хыиуду

BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Код на Delphi:

Код: Выделить всё

uses DateUtils;

var datetime_1, datetime_2 : TDateTime; 

 datetime_1 := EncodeDateTime(2007, 12, 12, 14, 56, 06, 0);
 datetime_2 := EncodeDateTime(2007, 12, 12, 14, 56, 10, 0);
   // Здесь результат будет: 4
 ShowMessage (IntToStr (SecondsBetween (datetime_1, datetime_2)));
  
 datetime_1 := EncodeDateTime(2007, 12, 12, 14, 53, 06, 0);
 datetime_2 := EncodeDateTime(2007, 12, 12, 14, 53, 10, 0);
   // А здесь результат будет: 3
 ShowMessage (IntToStr (SecondsBetween (datetime_1, datetime_2)));
Т.е. при вычислении разности двух моментов времени в секундах где-то происходит отбрасывание значащих цифр (а не округление, как мы увидим ниже). Ведь время в тиее данных TDateTime хранится как дробная часть числа. Причем, похоже, если число минут не меньше (10*n + 5), то результат получается верным, если меньше, то на единицу меньше. (Хотя глобальных экспериментов не проводил, м/б условие еще более тонкое)

Если же в предыдущем примере вычислить разность в миллисекундах,
то получим результаты 4000 и 3999.

Отсюда и можно следать предположение, что [в какой-то момент - ?] при вычислении секунд "лишние" цифры отбрасываются, а не округляются!

Т.е. более точная разность в секундах между двумя моментами времени получится при использовании:

round (SecondsMilliBetween (datetime_1, datetime_2) / 1000);
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Дейттвительно, исходный текст ф-и SecondsBetween выглядит как:

Код: Выделить всё

function SecondsBetween(const ANow, AThen: TDateTime): Int64;
begin
  Result := Trunc(SecondSpan(ANow, AThen));
end;
Так что, вместо
round (SecondsMilliBetween (datetime_1, datetime_2) / 1000);
проще сделать:
Round(SecondSpan(datetime_1, datetime_2));

Но тем не менее.
Можно сказать, что программист - это пользователь программы "компилятор Delphi".
И с точки зрения ЗДРАВОГО СМЫСЛА и с точки зрения пользователя, длина интервала
от 14:53:06 до 14:53:10 В ТОЧНОСТИ РАВНА длине интервала от 14:56:06 до 14:56:10.
Тем не менее программа "компилятор Delphi" утверждает, что длины этих
интервалов различны! Странная логика у этой программы :)
Аватара пользователя
somewhere
Сообщения: 1858
Зарегистрирован: 31 авг 2006, 17:14
Откуда: 71 RUS
Контактная информация:

&quot писал(а):Result := Trunc(SecondSpan(ANow, AThen));
Как то все закручено, хрен знает... как в мыльной опере, по идее разница в секундах это всеже
trunc((Athen - ANow)*86400) - т.е. количество полных секунд, там не надо округлять
It's a long way to the top if you wanna rock'n'roll
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

somewhere писал(а):Как то все закручено, хрен знает... как в мыльной опере, по идее разница в секундах это всеже
trunc((Athen - ANow)*86400) - т.е. количество полных секунд, там не надо округлять
Ну так когда они запихивают время в дробную часть, вот, видимо, тут-то у них погрешность и появляется. А потом аукается.
Вообще-то, раз уж они предоставляют класс, то меня, как пользователя этого класса, по идее, не должен волновать их способ хренения информации. Т.е. их класс для меня - черный ящик. Но на выходе, воспользовавшись функцями, обрабатывающими этот класс, я в данном сулчае получаю результат, противоречащий здравому смыслу.
Аватара пользователя
somewhere
Сообщения: 1858
Зарегистрирован: 31 авг 2006, 17:14
Откуда: 71 RUS
Контактная информация:

BBB, проверил, все сходиться, не стал брать точный номер дня, но взял какой то в этом году. Обьявил все переменные как Double, вот что получилось

a := 53766 / 86400 + 39426; //14:56:06
b := 53770 / 86400 + 39426; //14:56:10
c := 53586 / 86400 + 39426; //14:53:06
d := 53590 / 86400 + 39426; //14:53:10

b-a = 0,000046296299842652, в секундах *86400 = 4,0000003064051328
d-c = 0,000046296292566694, в секундах *86400 = 3,9999996777623616
Результат на лицо, нетрудно догаться что будет после Trunc ;)
It's a long way to the top if you wanna rock'n'roll
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

somewhere,
Кстати, делят они не на 86400, а на 86400000, так как там (в TDateTime) и миллисекунды хранятся. Оно, конечно, вроде, принципиально ничего не меняет, т.к. предварительно часы, минуты и секунды на лишнюю же 1000 и множатся. Только появляется больше значащих цифр после запятой, если кол-во миллисекунд не нулевое.

Код: Выделить всё

const
  HoursPerDay   = 24;
  MinsPerDay    = HoursPerDay * 60;
  SecsPerDay    = MinsPerDay * 60;
  MSecsPerDay   = SecsPerDay * 1000;

function TryEncodeTime(Hour, Min, Sec, MSec: Word; out Time: TDateTime): Boolean;
begin
  Result := False;
  if (Hour < 24) and (Min < 60) and (Sec < 60) and (MSec < 1000) then
  begin
    Time := (Hour * 3600000 + Min * 60000 + Sec * 1000 + MSec) / MSecsPerDay;
    Result := True;
  end;
end;
Аватара пользователя
Duncon
Сообщения: 2085
Зарегистрирован: 10 окт 2004, 14:11
Откуда: Питер
Контактная информация:

Ребят, вы о чем? Время помоему везде храниться в значениях с плавающей точкой (типа Double)..
Аватара пользователя
somewhere
Сообщения: 1858
Зарегистрирован: 31 авг 2006, 17:14
Откуда: 71 RUS
Контактная информация:

&quot писал(а):Ребят, вы о чем? Время помоему везде храниться в значениях с плавающей точкой (типа Double)..
Ну так мы об этом и говорим, что точности не хватает...
It's a long way to the top if you wanna rock'n'roll
Аватара пользователя
Duncon
Сообщения: 2085
Зарегистрирован: 10 окт 2004, 14:11
Откуда: Питер
Контактная информация:

Точность понятие относительная, сам подумай про скорость изменения времени в какой-нить +7 степени.. Думается скорее не хватает количества тиков проца за секунду для таких исчислений, вот и все любовь..
Аватара пользователя
somewhere
Сообщения: 1858
Зарегистрирован: 31 авг 2006, 17:14
Откуда: 71 RUS
Контактная информация:

Нет, не в этом дело. Тут проблема в самом формате представления даты/времени как в числе типа Double, где 1 день равен единице, а время фактически является дробной частью числа. Потеря точности дробной части начинает возникать при увеличении целой части - об этом гласит формат представления вещественных чисел в двоичном виде. Тики процессора с этим не связаны никак. В проце начиная с P166 реализован целочисленный 64 битный счетчик тиков (time stamp counter) который увеличивается на единицу каждый тактовый цикл поступающий от генератора. Этот счетчик работает независимо от выполняемых комманд. Он обнуляется только когда выполняется аппаратный сброс процессора, даже при тактовой частоте 3ГГц 64 бит счетчика хватит на 200 лет.
It's a long way to the top if you wanna rock'n'roll
Ответить