2014-11-19 3 views
6

Я разделив два двойников в .NET и использование объекта результата отработать дату окончания с даты начала по телефону (dtStart предопределена):Почему DateTime.AddDays округляется до ближайшей миллисекунды?

var dValue = 1500.0/8400.0; 
var dtEnd = dtStart.AddDays(dValue); 

После осмотра dtEnd я обнаружил, что результат был только с точностью до ближайшей миллисекунды. Посмотрев его, я обнаружил, что .AddMilliseconds и т. Д. Вокруг числа и TimeSpan.FromDays делает аналогичную вещь. Мне было интересно, была ли причина, почему это округление было сделано, поскольку кажется, что единственный способ получить правильное значение здесь - использовать .AddTicks?

Для ссылки .AddDays вызовов (где MillisPerDay = 86400000)

public DateTime AddDays(double value) 
{ 
    return Add(value, MillisPerDay); 
} 

который называет

private DateTime Add(double value, int scale) 
{ 
    long millis = (long)(value * scale + (value >= 0? 0.5: -0.5)); 
    if (millis <= -MaxMillis || millis >= MaxMillis) 
    throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue")); 
    return AddTicks(millis * TicksPerMillisecond); 
} 
+0

Это стоило мне довольно много времени отладки. Перед написанием некоторого кода, связанного с аудио, я подсчитал, что точности как double, так и 'TimeSpan' достаточно, чтобы однозначно идентифицировать конкретный образец. И тогда глупая функция «FromSeconds» решила округлить до миллисекунды ... – CodesInChaos

ответ

2

Edit: Подумав вещи снова, теперь я понимаю, первый вариант моего ответа был неправ.

Вот комментарии в исходном коде от Microsoft:

// Returns the DateTime resulting from adding a fractional number of 
// xxxxs to this DateTime. The result is computed by rounding the 
// fractional number of xxxxs given by value to the nearest 
// millisecond, and adding that interval to this DateTime. The 
// value argument is permitted to be negative. 

Эти комментарии появляются на пяти различных AddXxxxs (двойное значение) методы, где XXXX = дни, часы, миллисекунды, минуты и секунды.

Обратите внимание, что это только для методов, принимающих значение с плавающей запятой. (И можно задаться вопросом, привлекать ли значения с плавающей запятой в расчеты по дате, но это тема для другого дня.)

Теперь, как правильно указывает OP, эти пять методов все это называют метод:

// Returns the DateTime resulting from adding a fractional number of 
// time units to this DateTime. 
private DateTime Add(double value, int scale) { 
    long millis = (long)(value * scale + (value >= 0? 0.5: -0.5)); 
    if (millis <= -MaxMillis || millis >= MaxMillis) 
     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_AddValue")); 
    return AddTicks(millis * TicksPerMillisecond); 
} 

Так что делается в том, что значение добавляется к DateTime округляется до ближайшего числа миллисекунды перед добавлением. Но не результат - только добавленная стоимость (или вычитание).

Это фактически документировано, например http://msdn.microsoft.com/en-us/library/system.datetime.adddays%28v=vs.110%29.aspx «Параметр значения округлен до ближайшей миллисекунды».

Почему это делает я не знаю. Возможно, программисты полагали, что если вы используете значения с плавающей запятой, вы должны знать, что ваши значения, как правило, не совсем точны. Или, может быть, они хотят в какой-то степени имитировать стили Java-стиля, которые основаны на миллисекундах.

+0

Я бы ответил на исходный кодер реализации DateTime. Если они не масштабируются в миллисекундах, а непосредственно на количество тиков в день, я думаю, что мы получим ошибки округления при добавлении более 1042,5 дней. Это число исходит из максимального целочисленного диапазона, который может быть представлен точно двойным, 2^53 -1, деленным на TicksPerDay. Microsoft использует разрешение 100 нс, поэтому TicksPerDay составляет 24 * 60 * 60 * 1000 * 1000 * 100. Итак, моя догадка округляется до миллисекунды, что делает поведение более легким для прогнозирования. –

Смежные вопросы