Почему 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

Количество секунд с точностью до миллисекунды.

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

На правах спекуляции..

  1. TimeSpan.MaxValue.TotalMilliseconds равно 922337203685477. Номер, который имеет 15 цифр.
  2. double с точностью до 15 цифр.
  3. 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) знаками.Используйте отражатель для расследования

Другие вопросы по тегам