Pytanie TimeSpan FromMilliseconds dziwna implementacja?


Ostatnio napotkałem pewne zachowanie wird w implementacji zakresu czasu .net.

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

FromMilliseconds przyjmuje parametr double jako parametr. Wydaje się jednak, że wartość jest zaokrąglana wewnętrznie.

Jeśli utworzę nowy przedział czasowy z 5000 znacznikami (.5 ms), wartość TotalMilliseconds jest poprawna.

Patrząc na implementację TimeSpan w odbłyśniku, widać, że dane wejściowe są w rzeczywistości rzucane na długi czas.

Dlaczego firma Microsoft zaprojektowała metodę FromMilliseconds, aby zamiast parametru Długo przyjmowała podwójny parametr (ponieważ podwójna wartość jest bezużyteczna z uwagi na tę implementację)?


21
2018-03-27 16:11


pochodzenie


Brzmi jak zły projekt. Odpowiedź @ CodeNaked wskazuje, że jest udokumentowana, ale to oznacza, że ​​udokumentowali złe zachowanie. Jestem z tobą: to błąd. Jeśli napiszesz to w Connect, umieść link tutaj, aby ludzie mogli na niego głosować. - Joe White
connect.microsoft.com/VisualStudio/feedback/details/653782/... - Kasper Holdum


Odpowiedzi:


Pierwsza uwaga zastanawia się, dlaczego wybrali podwójnie jako wartość zwracana. Za pomocą długie byłby oczywistym wyborem. Chociaż istnieje już doskonale dobra właściwość, która jest długa, Ticks jest jednoznaczny z jednostką o wartości 100 nanosekund. Ale wybrali podwójnie, prawdopodobnie z zamiarem zwrotu wartości ułamkowej.

To jednak stworzyło nowy problem, który prawdopodobnie został odkryty dopiero później. Podwójny może przechowywać tylko 15 cyfr znaczących. TimeSpan może przechowywać 10 000 lat. To jest bardzo pożądane jest przekonwertowanie z TimeSpan na milisekundy, a następnie powrót do TimeSpan i uzyskanie tej samej wartości.

To nie jest możliwe z podwójnym. Wykonanie obliczeń matematycznych: 10 000 lat to w przybliżeniu 10000 x 365,4 x 24 x 3600 x 1000 = 315 705 000 000 000 milisekund. Odlicz 15 cyfr, najlepiej podwójnych, a otrzymasz dokładnie jedna milisekunda jako najmniejsza jednostka, którą można nadal przechowywać bez błędu zaokrągleń. Każda dodatkowa cyfra będzie losowym szumem.

Utkwił między skałą a twardym miejscem, projektanci (testerzy?) Musieli wybrać pomiędzy zaokrągleniem wartości podczas konwersji z TimeSpan na milisekundy. Lub zrobić to później, przechodząc od milisekund do TimeSpan. Postanowili zrobić to wcześniej, odważną decyzję.

Rozwiąż problem, korzystając z właściwości Ticks i mnożąc przez 1E-4, aby uzyskać milisekundy.


22
2018-03-27 17:14



Dziękuję, to odpowiada na pytanie! - Ilya Kogan
Nadal nie rozumiem, dlaczego nie po prostu wdrożyli go w sposób, który zachowuje precyzję. Coś jak: => this((long)milliseconds) + TimeSpan.FromTicks(1000*(milliseconds - ((long)milliseconds))) - Alain


Jest to oczywiście zgodne z projektem. The dokumentacja mówi tak samo:

Parametr wartości jest konwertowany na   kleszcze i ta liczba kleszczy jest   używany do inicjalizacji nowego TimeSpan.    Dlatego wartość będzie tylko   uważane za dokładne z dokładnością do najbliższego   milisekundę.


3
2018-03-27 16:21



Wartości są oczywiście przekształcone w kleszcze. Jakkolwiek, robi się to po konwersji podanej wartości na długą. - DEHAAS
@DEHAAS - Zgadza się. Niezależnie od użytej metody "Od" dokładność jest ograniczona do najbliższej milisekundy. Po prostu odrąbują ułamkową wartość, rzucając ją na długo. - CodeNaked
@DEHAAS - Prawdopodobnie wybierali podwójne jako typ parametru, więc "pasowali" do innych metod From. Możesz zrobić FromSeconds (0.5), aby uzyskać 5000ms. Logicznie rzecz biorąc, ma sens użycie podwójnego dla FromMilliseconds, nawet jeśli można to zrobić z int / long. - CodeNaked
@ CodeNaked Zdaję sobie z tego sprawę. Właśnie zastanawiałem się, dlaczego Microsoft wybrał taką implementację. Zwłaszcza przy wyborze tego projektu, dlaczego metoda FromMilliseconds bierze podwójne, zamiast długiego, ponieważ wszelkie dodatkowe informacje przechowywane w długiej zmiennej zawsze są tracone. - DEHAAS
@ CodeNaked Dzięki za wskazanie tego, jest to bardzo mylące. Napotkałem to dziwne zachowanie z FromMilliseconds, ale nie wiedziałem, że inne metody From zachowały się w ten sam sposób. - Ilya Kogan


Zaakceptowanie podwójnego jest logicznym projektem. Możesz mieć ułamki milisekund.

To, co się dzieje wewnętrznie, to projekt implementacji. Nawet jeśli wszystkie obecne implementacje (CLI) zaokrąglają to najpierw, nie musi tak być w przyszłości.


2
2018-03-27 16:17



Zastosowanie podwójnej wartości z pewnością sprawia, że ​​od tego czasu. Wydaje się jednak, że spowoduje to "przełomowe" zmiany w implementacji TimeSpan. - DEHAAS


Problem z kodem jest w rzeczywistości pierwszą linią, do której dzwonisz FromMilliseconds. Jak wspomniano wcześniej, uwagi w dokumentacji zawierają następujące informacje:

The wartość Parametr jest konwertowany na znaczniki i ta liczba znaczników jest używana do zainicjowania nowego TimeSpan. W związku z tym, wartość będą uważane za dokładne z dokładnością do milisekundy.

W rzeczywistości stwierdzenie to nie jest poprawne ani logiczne. W odwrotnej kolejności:

  • Kleszcze są definiowane jako "sto nanosekund". Zgodnie z tą definicją dokumentacja powinna być napisana jako:

    W związku z tym, wartość będą uważane za dokładne tylko do najbliższego milisekundę  kleszcza lub jedną dziesiątą milionową sekundy.

  • Z powodu błędu lub niedopatrzenia, wartość parametr nie jest konwertowany bezpośrednio na tiki przed zainicjowaniem nowej instancji TimeSpan. To widać w źródle odniesienia dla TimeSpan, gdzie millis wartość jest zaokrąglana wcześniejszy do jego konwersji na kleszcze, a nie po. Jeśli zachowano maksymalną precyzję, ten wiersz kodu powinien brzmieć następująco (a korekta o 0,5 milisekundy 3 wiersze wcześniej zostałyby usunięte):

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

Podsumowanie:

Dokumentacja dla różnych TimeSpan.From*, z wyjątkiem FromTicks, należy zaktualizować, aby stwierdzić, że argument jest zaokrąglany do najbliższej milisekundy (bez uwzględnienia odniesienia do kleszczy).


1
2017-08-08 14:36





Lub możesz zrobić:

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

--sarkazm

TimeSpan konwertuje podwójną liczbę milisekund na takty "OCZYWIŚCIE"możesz mieć TimeSpan z mniejszą niż 1ms ziarnistością.

-/sarkazm

- to wcale nie jest oczywiste ... dlaczego tak się nie dzieje w metodzie .FromMilliseconds jest poza mną.


0
2018-05-29 15:12