Точность и погрешность DateTime
Структура DateTime представляет дату в виде 64-разрядного значения, которое содержит количество «тиков», прошедшее с некоторой определенной даты. Одна секунда состоит из десяти миллионов тиков.
Это весьма высокий уровень точности (precision). С помощью структуры DateTime вы можете представлять дату и время с точностью до долей микросекунд, что обычно превосходит требуемую. Хотя, конечно же, не всегда; на современном оборудовании за один тик вы сможете выполнить несколько сотен инструкций, так что, если вам нужны временные интервалы с точностью, соответствующей отдельной инструкции, то тики, конечно же, являются слишком грубой единицей измерения.
С такой высокой точностью возникает проблема, поскольку мы легко можем предположить, что заданное значение имеет погрешность (accuracy), соответствующую точности (presision). Но этого вообще-то никто не гарантирует! Я могу представить свой рост в переменной с плавающей точкой двойной точности как 1.799992352094 метров; с точностью до триллионной доли метра, но погрешность составит сотые доли метра, поскольку у меня просто нет устройства, чтобы измерить мой рост с точностью до триллионной или даже тысячной доли метра. В этом случае погрешность превосходит точность.
Это же справедливо и для даты/времени. Ваша структура DateTime может иметь точность на уровне долей микросекунды, но какая будет при этом погрешность? Я постоянно синхронизирую свои компьютеры с time.gov. Но если этого не делать, то мои часы уходят на несколько секунд в год. Предположим, что мои часы уходят на одну секунду в год. В году 31.5 миллиона секунд, а в одной секунде 10 миллионов тиков, т.е. часы уходят на один тик каждые 3.15 секунды. Даже если в определенный момент времени мои часы будут выдавать невероятно точное время на уровне тиков, то за десять секунд их время опять станет не точным. А за день большая часть точности превратится в мусор.
Если вы немного поэкспериментируете, то увидите, что когда вы спрашиваете у операционной системы «который час?», то она обеспечивает погрешность в тысячи раз превосходящую точность.
long ticks = DateTime.Now.Ticks;
while(true)
{
if (ticks != DateTime.Now.Ticks)
{
ticks = DateTime.Now.Ticks;
Console.WriteLine(ticks);
}
else
{
Console.WriteLine("same");
}
}
На моем компьютере этот код выводит «same» восемь или девять раз подряд, а затем внезапно свойство Ticks увеличивается примерно на 160000, т.е. на 16 миллисекунд или на одну 64-ю секунды. (На разных версиях Windows результаты могут отличаться, в зависимости от особенностей алгоритма работы планировщика потоков и других деталей реализации.)
Как видите, хотя кажется, что точность часов составляет доли микросекунд, на практике вы получаете точность в 16 миллисекунд. (И, конечно же, погрешность зависит от того, насколько точно они синхронизированы с эталонным временем.)
Является ли эта проблема недостатком DateTime.Now? На самом деле, нет. Цель «настенных часов» – предоставить дату/время для обычных нужд, например, для ответа на вопросы «Когда начинается фильм «Доктор Кто»?», или «Когда мы переходим с летнего времени на зимнее?», «Покажи мне документы, которые я редактировал в последний четверг после обеда». Это не те операции, которые требуют точности в доли микросекунд.
(И кстати, в VBScript метод получения текущего времени, встроенный в этот язык, округляет время, полученное от операционной системы до секунды, а не до 1/64-oй доли секунды.)
Одним словом, на вопрос: «Который час?» можно ответить только с точностью, которая отвечает погрешности определенной системы. Большинство компьютерных часов не синхронизируются с мировым временем даже на уровне миллисекунд, поэтому точность, которая будет превосходить этот уровень, в любом случае является ложью. Мне кажется довольно неудачным тот факт, что структура DateTime поддерживает такую точность, поскольку это создает иллюзию, что ее операции должны поддерживать соответствующую погрешность. Но они определенно на это не способны.
Но вопрос: «Сколько времени прошло между началом и завершением чего-то?» не имеет ничего общего с вопросом: «Который час?». Если вопрос сводится к получению длительности операции, и вы хотите получить ответ с высокой точностью и низкой погрешностью, тогда вам следует использовать класс Stopwatch. Этот класс действительно имеет наносекундную точность и соизмеримую с этой величиной погрешность.
Помните, что вам не нужно знать о том, который час, чтобы узнать, сколько времени прошло. Это могут быть две совершенно разные вещи.
Comments
Anonymous
December 09, 2010
Здравствуйте! У меня есть вопрос, почему структуры типа Datetime могут вычитаться друг из друга, а обычные структуры нельзяAnonymous
February 01, 2011
Наверное, потому что в DateTime перегружен оператор вычитания, а в структурах, где вычитание по смыслу и логике не нужно, - перегрузку оператора вычитания не делают.