Почему TimeSpan.FromSeconds(double) округляется до миллисекунд?
TimeSpan.FromSeconds
занимает удвоение и может представлять значения до 100 наносекунд, однако этот метод необъяснимым образом округляет время до целых миллисекунд.
Учитывая, что я только что потратил полчаса, чтобы точно определить это (задокументированное!) Поведение, знание того, почему это может иметь место, поможет легче мириться с потерянным временем.
Кто-нибудь может подсказать, почему это, казалось бы, контрпродуктивное поведение реализовано?
TimeSpan.FromSeconds(0.12345678).TotalSeconds
// 0.123
TimeSpan.FromTicks((long)(TimeSpan.TicksPerSecond * 0.12345678)).TotalSeconds
// 0.1234567
5 ответов
Как вы сами узнали, это документированная функция. Это описано в документации TimeSpan:
параметры
Типзначения: System.Double
Количество секунд с точностью до миллисекунды.
Причина этого, вероятно, в том, что двойное число не совсем точно. Всегда полезно сделать некоторое округление при сравнении двойных чисел, потому что оно может быть чуть-чуть больше или меньше, чем вы ожидаете. Такое поведение может на самом деле дать вам несколько неожиданных наносекунд, если вы попытаетесь использовать целые миллисекунды. Я думаю, что именно по этой причине они решили округлить значение до целых миллисекунд и отбросить меньшие цифры.
На правах спекуляции..
TimeSpan.MaxValue.TotalMilliseconds
равно 922337203685477. Номер, который имеет 15 цифр.double
с точностью до 15 цифр.TimeSpan.FromSeconds
,TimeSpan.FromMinutes
и т. д. все проходят преобразование в миллисекунды, выраженные вdouble
(то к галочкам то кTimeSpan
что не интересно сейчас)
Итак, когда вы создаете TimeSpan
это будет близко к TimeSpan.MaxValue
(или же MinValue
) преобразование будет с точностью до миллисекунд.
Таким образом, вероятный ответ на вопрос "почему" таков: всегда иметь одинаковую точность.
Еще одна вещь, о которой стоит подумать, это то, можно ли было бы выполнить работу лучше, если бы преобразования выполнялись посредством первого преобразования значения в тики, выраженные в long
,
Представьте, что вы разработчик, ответственный за разработку TimeSpan
тип. У вас есть все основные функции на месте; кажется, все работает отлично. Затем однажды приходит бета-тестер и показывает вам этот код:
double x = 100000000000000;
double y = 0.5;
TimeSpan t1 = TimeSpan.FromMilliseconds(x + y);
TimeSpan t2 = TimeSpan.FromMilliseconds(x) + TimeSpan.FromMilliseconds(y);
Console.WriteLine(t1 == t2);
Почему этот вывод False
? тестер спрашивает вас. Даже если вы понимаете, почему это произошло (потеря точности при сложении x
а также y
), вы должны признать, что это кажется немного странным с точки зрения клиента. Затем он бросает это в вас:
x = 10.0;
y = 0.5;
t1 = TimeSpan.FromMilliseconds(x + y);
t2 = TimeSpan.FromMilliseconds(x) + TimeSpan.FromMilliseconds(y);
Console.WriteLine(t1 == t2);
Что один выводит True
! Тестер по понятным причинам скептически.
На данный момент вы должны принять решение. Либо вы можете разрешить арифметическую операцию между TimeSpan
значения, которые были построены из double
значения для получения результата , точность которого превышает точность double
введите сам - например, 100000000000000.5 (16 значащих цифр) - или вы можете, вы знаете, не допустить этого.
Итак, вы решаете, вы знаете, что я просто сделаю так, чтобы любой метод, который использует double
построить TimeSpan
будет округлено до ближайшей миллисекунды. Таким образом, четко задокументировано, что преобразование из double
к TimeSpan
это операция с потерями, освобождающая меня в тех случаях, когда клиент видит странное поведение, подобное этому, после конвертации из double
в TimeSpan
и надеясь на точный результат.
Я не обязательно утверждаю, что это "правильное" решение здесь; ясно, что этот подход сам по себе вызывает некоторую путаницу. Я просто говорю, что решение нужно было принять тем или иным способом, и это то, что, по-видимому, было решено.
Я думаю, что объяснение есть: структура TimeSpan неправильно обрабатывает значения, близкие к минимальному и максимальному значению
И похоже, что это не изменится в ближайшее время:-)
FromSeconds использует приватный метод Interval
public static TimeSpan FromSeconds(double value)
{
return Interval(value, 0x3e8);
}
0x3e8 == 1000
Интервал метод многопользовательского значения на этом const и затем приведение к long (см. Последнюю строку):
private static TimeSpan Interval(double value, int scale)
{
if (double.IsNaN(value))
{
throw new ArgumentException(Environment.GetResourceString("Arg_CannotBeNaN"));
}
double num = value * scale; // Multiply!!!!
double num2 = num + ((value >= 0.0) ? 0.5 : -0.5);
if ((num2 > 922337203685477) || (num2 < -922337203685477))
{
throw new OverflowException(Environment.GetResourceString("Overflow_TimeSpanTooLong"));
}
return new TimeSpan(((long) num2) * 0x2710L); // Cast to long!!!
}
В результате мы имеем точность с 3 (x1000) знаками.Используйте отражатель для расследования