Почему TimeSpan.FromSeconds (double) округляется до миллисекунд? - PullRequest
34 голосов
/ 12 января 2011

TimeSpan.FromSeconds принимает удвоение и может представлять значения до 100 наносекунд, однако этот метод необъяснимым образом округляет время до целых миллисекунд.

Учитывая, что я потратил полчаса, чтобы точно определить это (задокументированное!) Поведение, зная, почему это может иметь место, было бы легче мириться с потраченным временем.

Кто-нибудь может подсказать, почему реализовано это, казалось бы, непродуктивное поведение?

TimeSpan.FromSeconds(0.12345678).TotalSeconds
    // 0.123
TimeSpan.FromTicks((long)(TimeSpan.TicksPerSecond * 0.12345678)).TotalSeconds
    // 0.1234567

Ответы [ 5 ]

11 голосов
/ 21 января 2011

Как вы сами узнали, это документированная функция.Это описано в документации TimeSpan :

Параметры

значение Тип: System.Double

Количество секунд, с точностью до ближайшей миллисекунды .

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

7 голосов
/ 21 января 2011

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

  1. TimeSpan.MaxValue.TotalMilliseconds равно 922337203685477. Число, состоящее из 15 цифр.
  2. double точно до 15 цифр.
  3. TimeSpan.FromSeconds, TimeSpan.FromMinutes и т. Д. Все проходят преобразование в миллисекунды, выраженные в double (затем в галочки, затем в TimeSpan, что сейчас не интересно)

Таким образом, когда вы создаете TimeSpan, который будет близок к TimeSpan.MaxValue (или MinValue), преобразование будет точным до миллисекунд * только 1021 *.
Так что вероятный ответ на вопрос «почему» означает: , чтобы иметь одинаковую точность все время .
Еще одна вещь, о которой стоит подумать, может ли работа иметьбыло бы лучше, если бы преобразование осуществлялось путем первого преобразования значения в тики, выраженные в long.

4 голосов
/ 22 января 2011

Представьте, что вы разработчик, ответственный за разработку типа 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 и надеясь на точный результат.

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

1 голос
/ 20 января 2011

Я думаю, что есть объяснение: Структура TimeSpan неправильно обрабатывает значения, близкие к минимальному и максимальному значению

И похоже, что в ближайшее время она не изменится: -)

0 голосов
/ 21 января 2011

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) знаками. Используйте рефлектор для расследования

...