2011-01-12 3 views
33

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

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

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

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

Простите, что старый ответ уходит. Я сомневаюсь, что я мог бы придумать что-нибудь получше (и я хотел бы это сделать +1) – Peter

+0

«[K] учитывая, почему это может быть так, было бы легче мириться с потерянным временем». Подумайте, это утомительная стоимость. – jason

+1

Убит от этого тоже. Моя теория заключается в том, что это была ошибка в .net 1 и не была изменена, поскольку она нарушила существующие программы. IMO MS должна хотя бы обновить описание intellisense, указав, что эти функции имеют только миллисекундную точность. – CodesInChaos

ответ

11

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

Параметры

значение Тип: System.Двойной

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

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

+0

«Неожиданные наносекунды» звучат как правдоподобная теория. Все еще не оправдывает это в _my_ книге, но это не относится к делу. +1. –

+0

Да, но миллисекунд, вероятно, будет достаточно для большинства людей. Люди, которые достаточно умны для работы с наносекундами, могут использовать 'TimeSpan.FromTicks (TimeSpan.TicksPerSecond * YourSecondsDouble)'. ;) – GolezTrol

0

FromSeconds использует частный метод Interval

public static TimeSpan FromSeconds(double value) 
{ 
    return Interval(value, 0x3e8); 
} 

0x3e8 == 1000

метод значение интервала Multiplay на этом сопзЬ и затем отливали долго (см последнюю строку):

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

+0

Я, конечно же, посмотрел. Отражатель всегда может ответить на _how_, но не _why_. Помогло ли это вам выяснить, почему приведение до конца происходит ** до ** умножения?Я бы подумал, что ошибка была не документирована. Возможно, это была ошибка, которую легче документировать, чем исправлять. Итог, этот код не отвечает на вопрос «почему». –

+1

@romkyns «Ошибка легче документировать, чем исправлять!» Я думаю, вы только что нашли новый лозунг Microsoft! ;-) –

+0

@Nicolas: Как отдельная фраза это звучит забавно. Но я не думаю, что здесь есть ошибка _unexpected_. Этот дизайн может быть преднамеренным. –

7

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

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

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

+0

Согласно документам, значение * * сначала преобразуется в тики. – GolezTrol

+0

@ Golez: Где вы видели такой документ? [Параметр значения преобразуется в миллисекунды, который преобразуется в тики, а это число тиков используется для инициализации нового TimeSpan] (http://msdn.microsoft.com/en-us/library/system.timespan.fromseconds .aspx) –

+0

Извините, я неправильно понял. – GolezTrol

4

Представьте, что вы являетесь разработчиком, отвечающим за проектирование типа 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 типа -eg, 100000000000000.5 (16 значащих цифр) -ила вы можете, вы есть, нет это можно сделать.

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

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

+0

Вы можете использовать этот самый аргумент, чтобы запретить удвоение вообще ... Но тогда я могу представить, что дизайнеры неоправданно обращают внимание на абсолютных новичков при тестировании этого конкретного класса ... Хотелось бы знать, как это произошло:) –

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