now new net c# .net timespan

c# - new - TimeSpan FromMilliseconds ¿Implementación extraña?



timespan now c# (5)

Aceptar un doble es un diseño lógico. Puedes tener fracciones de milisegundos.

Lo que está sucediendo internamente es un diseño de implementación. Incluso si todas las implementaciones actuales (de la CLI) lo redondean primero, no tiene por qué ser el caso en el futuro.

Recientemente me encontré con un comportamiento extraño en la implementación de la extensión de tiempo .net.

TimeSpan test = TimeSpan.FromMilliseconds(0.5); double ms = test.TotalMilliseconds; // Returns 0

El FromMilliseconds toma un doble como parámetro. Sin embargo, parece que el valor se redondea internamente.

Si realizo una instancia de un nuevo intervalo de tiempo con 5000 tics (.5 ms), el valor de TotalMilliseconds es correcto.

Al observar la implementación de TimeSpan en el reflector, se revela que, de hecho, la entrada se convierte en un largo.

¿Por qué Microsoft diseñó el método FromMilliseconds para tomar un doble parámetro en lugar de un largo (ya que un doble valor es inútil dada esta implementación)?


El problema con su código es en realidad la primera línea, donde llama a FromMilliseconds . Como se señaló anteriormente, las observaciones en la documentación establecen lo siguiente:

El parámetro de valor se convierte en tics y ese número de tics se usa para inicializar el nuevo TimeSpan. Por lo tanto, el valor solo se considerará exacto al milisegundo más cercano.

En realidad, esta afirmación no es correcta ni lógica. En orden inverso:

  • Las garrapatas se definen como "cien nanosegundos". Según esta definición, la documentación debería haberse escrito como:

    Por lo tanto, el valor solo se considerará exacto al más cercano milisegundo garrapata, o una diez millonésima de segundo .

  • Debido a un error o descuido, el parámetro de valor no se convierte directamente en tics antes de inicializar la nueva instancia de TimeSpan. Esto se puede ver en la fuente de referencia de TimeSpan , donde el valor de millis se redondea antes de su conversión a tics, en lugar de después. Si se mantuviera la máxima precisión, esta línea de código debería haberse leído de la siguiente manera (y se eliminaría el ajuste en 0,5 milisegundos 3 líneas anteriores):

    return new TimeSpan((long)(millis * TicksPerMillisecond));

Resumen:

La documentación para los diversos TimeSpan.From* , con la excepción de FromTicks , debe actualizarse para indicar que el argumento se redondea al milisegundo más cercano (sin incluir la referencia a las marcas).


Esto es por diseño, obviamente. La documentation dice tanto:

El parámetro de valor se convierte en tics y ese número de tics se usa para inicializar el nuevo TimeSpan. Por lo tanto, el valor solo se considerará exacto al milisegundo más cercano.


La primera consideración es preguntarse por qué seleccionaron un doble como valor de retorno. Usar mucho tiempo hubiera sido una elección obvia. Aunque ya existe una propiedad perfectamente buena que es larga, Ticks no es ambigua con una unidad de 100 nanosegundos. Pero eligieron el doble, probablemente con la intención de devolver un valor fraccionario.

Sin embargo, eso creó un nuevo problema, uno que posiblemente solo se descubrió más tarde. Un doble puede almacenar solo 15 dígitos significativos. Un TimeSpan puede almacenar 10.000 años. Es muy conveniente convertir de TimeSpan a milisegundos, luego volver a TimeSpan y obtener el mismo valor.

Eso no es posible con un doble. Haciendo los cálculos: 10,000 años son aproximadamente 10000 x 365.4 x 24 x 3600 x 1000 = 315,705,600,000,000 milisegundos. Cuente 15 dígitos, lo mejor que puede hacer un doble, y obtiene exactamente un milisegundo como la unidad más pequeña que aún puede almacenarse sin error de redondeo. Cualquier dígito extra será ruido aleatorio.

Atrapados entre una roca y un lugar difícil, los diseñadores (¿probadores?) Tuvieron que elegir entre redondear el valor al convertir de TimeSpan a milisegundos. O hacerlo más tarde cuando se pasa de milisegundos a TimeSpan. Eligieron hacerlo temprano, una decisión valiente.

Resuelve tu problema usando la propiedad Ticks y multiplica por 1E-4 para obtener milisegundos.


O bien, podrías hacer:

double x = 0.4; TimeSpan t = TimeSpan.FromTicks((long)(TimeSpan.TicksPerMillisecond * x)); // where x can be a double double ms = t.TotalMilliseconds; //return 0.4

--sarcasmo

TimeSpan convierte el doble de milisegundos en tics, por lo que " OBVIAMENTE " puede tener un TimeSpan con menos de 1 ms de granularidad.

-/sarcasmo

- esto no es obvio en absoluto ... por qué esto no se hace dentro del método .FromMilliseconds está más allá de mí