Ошибка .NET AddDays
Следующие две строки добавляют ту же сумму к одной и той же дате, а часть даты результатов одна и та же, но как-то разница в части времени!
(new DateTime(2000,1,3,18,0,0)).AddDays(4535);
(new DateTime(2000,1,3,18,0,0)).AddMonths(149);
вы получите разницу в 15 секунд, и с обоими как минимум округлыми до нескольких дней, я не знаю, почему это произошло, но это происходит только с AddDays, но не с AddMonths (даже с добавлением тысяч месяцев)
Изменить 1
Итак, я попытался создать образец проекта, но не повезло. Если я запустил свой основной проект и поместил образцы строк в часы, я получу 2 отдельных значения, если я начну новый старт, проблемы там нет. Проект - 3.5, С#, vs2010, win7hp x64 (proj: x86). Я пытаюсь воспроизвести его также в новом маленьком проекте, я буду писать, если у меня есть.
Это мои результаты в основном проекте (copeid from watches!):
(new DateTime(2000, 1, 3, 18, 0, 0)).AddDays(4535).Ticks
634743432153600000 long
(new DateTime(2000, 1, 3, 18, 0, 0)).AddMonths(149).Ticks
634743432000000000 long
Изменить 2
Мне удалось еще больше сузить его. У нас есть самодельный компонент, панель, мы рисуем на нем с помощью directx. Если я сделаю видимым = false, чем visible = true, чем ошибка, то перед видимым = true (или show()) вычисление будет правильным. Что в мире может быть там, что результат получает что-то другое формулы, где не используется переменная. Культура не затрагивается в компоненте.
Ответы
Ответ 1
Это результат того, что DirectX молчаливо меняет режим вычисления с плавающей запятой CPU, чтобы всегда использовать одинаковую точность. Это иногда делается для производительности: использование одинарной точности может быть немного быстрее, чем использование двойной точности. См. Описание флага FpuPreserve
в документации MSDN для перечисления DirectX CreateFlags
.
Причина, по которой другие не могут воспроизвести это, заключается в том, что они не выполняют эти вызовы DirectX.
Аргумент AddDays
имеет значение double
. Это значение умножается на коэффициент масштабирования, чтобы получить время в миллисекундах. Именно этот расчет вызывает ошибку.
Рассмотрим:
double value = 4535;
int scale = 86400000;
long milliseconds = (long) ((value * scale) + ((value >= 0.0) ? 0.5 : -0.5));
long milliseconds2 = (long)((float)(value * scale) + ((value >= 0.0) ? 0.5 : -0.5));
Console.WriteLine(milliseconds2 - milliseconds);
Выражение для milliseconds2
содержит приведение к float
, которое имитирует эффект DirectX, который заставляет вычисления с одной точностью. Это напечатает 15360
, точно разницу, которую вы найдете.
В отличие от этого, AddMonths
принимает целое число и не использует арифметику с плавающей запятой. Таким образом, результат является точным.
Ответ 2
Здесь они дают тот же результат:
var d1 = (new DateTime(2000, 1, 3, 18, 0, 0)).AddDays(4535).Ticks;
var d2 = (new DateTime(2000, 1, 3, 18, 0, 0)).AddMonths(149).Ticks;
d1 == d2 == 634743432000000000
(Tick является внутренним "квантом" времени DateTime.Он довольно короткий. одна десятимиллионная секунда
Я добавлю, что даже Mono (независимая реализация .NET) дает тот же результат http://ideone.com/krySY (Ideone использует моно)
Учитывая более свежие вещи, которые вы написали, это довольно просто: повреждение памяти. Повреждение памяти может делать очень случайные вещи. Вероятно, это один из следующих: -)