2011-01-12 15 views
33

TimeSpan.FromSeconds toma un doble, y puede representar valores de hasta 100 nanosegundos, sin embargo, este método inexplicablemente redondea el tiempo a milisegundos enteros.¿Por qué TimeSpan.FromSeconds (doble) ronda a milisegundos?

Teniendo en cuenta que acabo de pasar media hora para identificar este comportamiento (¡documentado!), Saber por qué podría ser así sería más fácil soportar el tiempo perdido.

¿Alguien puede sugerir por qué se implementa este comportamiento aparentemente contraproducente?

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

Lamento ver desaparecer la respuesta anterior. Dudo que pudiera encontrar algo mejor (y quería +1 ...) – Peter

+0

"[K] ahora, por qué este podría ser el caso, sería más fácil soportar el tiempo perdido". Considéralo un costo hundido. – jason

+1

También lo mordieron. Mi teoría es que era un error en .net 1 y no se ha modificado porque rompería los programas existentes. La MS IMO debería al menos actualizar la descripción intellisense para indicar que estas funciones solo tienen una precisión de milisegundos. – CodesInChaos

Respuesta

11

Como usted mismo ha descubierto, es una función documentada. Es descrito en el documentation of TimeSpan:

Parámetros

valor Tipo: System.Doble

Un número de segundos, con precisión de milisegundo más cercano.

La razón de esto es probablemente porque un doble no es tan preciso en absoluto. Siempre es una buena idea hacer un poco de redondeo al comparar los dobles porque podría ser un poco más grande o más pequeño de lo que cabría esperar. Ese comportamiento en realidad podría proporcionarle algunos nanosegundos inesperados cuando intente poner en milisegundos enteros. Creo que esa es la razón por la que eligieron redondear el valor a milisegundos enteros y descartar los dígitos más pequeños.

+0

"nanosegundos inesperados" suena como una teoría plausible. Todavía no lo justifica en mi libro, pero eso no viene al caso. +1. –

+0

Sí, pero los milisegundos probablemente serán suficientes para la mayoría de las personas. Las personas que son lo suficientemente inteligentes como para trabajar con nanosegundos pueden usar 'TimeSpan.FromTicks (TimeSpan.TicksPerSecond * YourSecondsDouble)'. ;) – GolezTrol

0

FromSeconds utiliza método privado Intervalo

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

0x3e8 == 1000

intervalo de valores método multijugador en ese const y luego arrojado a tiempo (véase la última fila):

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!!! 
} 

Como resultado tenemos la precisión con 3 signos (x1000). Uso reflector para investigar

+0

Lo hice, por supuesto, mira. El reflector siempre puede responder al _how_, pero no al _why_. ¿Te ayudó esto a descubrir por qué ocurre el yeso antes de ** antes de la multiplicación?Consideraría que un error no estaba documentado. Tal vez todavía era un error que era más fácil de documentar que solucionar. En pocas palabras, este código no responde la pregunta "por qué". –

+1

@romkyns "¡Es más fácil documentar un error que solucionarlo!" ¡Creo que acabas de encontrar el nuevo eslogan de Microsoft! ;-) –

+0

@Nicolas: como frase separada, esto suena divertido. Pero no creo que estemos teniendo un error inesperado aquí. Este diseño puede ser intencional. –

7

sobre los derechos de una especulación ..

  1. TimeSpan.MaxValue.TotalMilliseconds es Ecuat a 922337203685477. El número que tiene 15 dígitos.
  2. double tiene exactamente 15 dígitos.
  3. TimeSpan.FromSeconds, etc. TimeSpan.FromMinutes todos pasan por la conversión de milisegundos expresadas en double (entonces a las garrapatas luego a TimeSpan que no es interesante ahora)

Por lo tanto, cuando se crea TimeSpan que estar cerca de TimeSpan.MaxValue (o MinValue) la conversión será precisa a milisegundos solamente.
Así que la respuesta probable a la pregunta "por qué" es: que tienen la precisión misma todos los tiempos.
Otra cosa en que pensar es si el trabajo podría haber sido mejor si las conversiones se hubieran hecho convirtiendo primero el valor en marcas expresadas en long.

+0

De acuerdo con los documentos, el valor * se * convierte a tics primero. – GolezTrol

+0

@Golez: ¿Dónde viste ese documento? [El parámetro de valor se convierte a milisegundos, que se convierte en ticks, y ese número de ticks se usa para inicializar el nuevo TimeSpan] (http://msdn.microsoft.com/en-us/library/system.timespan.fromseconds .aspx) –

+0

Lo siento, lo leí mal. – GolezTrol

4

Imagine que es el desarrollador responsable del diseño del tipo TimeSpan. Tienes toda la funcionalidad básica en su lugar; todo parece estar funcionando bien. Entonces, un día algunos beta tester viene y se muestra este código:

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); 

¿Por qué esa salida False? el probador le pregunta. Aunque comprenda por qué sucedió esto (la pérdida de precisión al sumar x y y), debe admitir que parece un poco extraño desde la perspectiva del cliente. Luego se lanza en este caso de que:

x = 10.0; 
y = 0.5; 
t1 = TimeSpan.FromMilliseconds(x + y); 
t2 = TimeSpan.FromMilliseconds(x) + TimeSpan.FromMilliseconds(y); 
Console.WriteLine(t1 == t2); 

que uno True salidas! El probador es comprensiblemente escéptico.

En este punto usted tiene que tomar una decisión. Cualquiera puede permitir una operación aritmética entre TimeSpan valores que se han construido a partir de double valores para producir un resultado cuya precisión excede la exactitud del tipo double -por ejemplo, 100000000000000.5 (16 cifras significativas) -o que puede, saber, no permitir eso.

Así que usted decide, usted sabe qué, lo haré de modo que cualquier método que use un double para construir un TimeSpan se redondeará al milisegundo más cercano. De esta manera, es documentado explícitamente que la conversión de double a TimeSpan es una operación con pérdida, absolviéndome en los casos en que un cliente ve un comportamiento extraño como este después de la conversión de double a TimeSpan y la esperanza de un resultado preciso.

No estoy necesariamente argumentando que esta es la decisión "correcta" aquí; claramente, este enfoque causa cierta confusión por sí mismo. Solo digo que se debe tomar una decisión de una manera u otra, y esto es lo que aparentemente se decidió.

+0

Podrías usar este mismo argumento para prohibir por completo los dobles ... Pero entonces, puedo imaginar a los diseñadores prestando excesiva atención a los principiantes absolutos mientras prueban esta clase específica ... Ojalá supiéramos con certeza cómo surgió esto:) –

Cuestiones relacionadas