2016-08-15 8 views
5

Za każdym razem, gdy potrzebujemy dużej dokładności dziesiętnej, do obliczeń używamy miejsc dziesiętnych. Czy istnieje sposób sprawdzenia, czy precyzja wystarczyła do obliczeń?Jak uzyskać wyjątek dotyczący niedokładnych obliczeń?

Chciałbym zrobić poniższy kod wyjątek:

decimal almostMax = Decimal.MaxValue - 1; 
decimal x = almostMax + 0.1m; // This should create an exception, since x equals almostMax. 
Assert.AreEqual(x, almostMax); // This does NOT fail. 

To naprawdę nie ma znaczenia w prawdziwym kodzie, ale byłoby miło, aby być bezpieczne.

+0

Ułamki dziesiętne mają dokładność 28 cyfr (niektóre, ale nie wszystkie, wartości mogą zawierać 29 cyfr). Jako Decimal.MaxValue jest numerem 28digit, dodając 0.1m przekracza precyzję dziesiętnej, dlatego te dwie wartości są równe i Assert nie zawiedzie. – PaulF

+2

@PaulF, myślę, że OP jest tego świadomy. Szuka sposobu na wykrycie tego problemu podczas uruchamiania. –

+0

Należy pamiętać, że wyjątek jest operacją wagi ciężkiej i powinien być używany tylko do rzeczy, które nie powinny się zdarzyć. Jeśli jest prawdopodobne, że niektóre obliczenia zawiodą, powinieneś zamiast tego użyć wzorca takiego jak "TryParse", który zwraca wartość boolean zamiast rzucania i będzie znacznie szybszy. – Lukos

Odpowiedz

3

Ta metoda rozszerzenie powinno pomóc. Odwraca operację i sprawdza, czy argumenty wejściowe można poprawnie obliczyć z wyniku. Jeśli tak nie jest, operacja spowodowała utratę precyzji.

przykład robocza: https://dotnetfiddle.net/vx6UYY

Jeśli chcesz korzystać ze zwykłych operatorów jak + etc, trzeba iść z Philipp Schmid's solution i wdrożenia operatorów na swoim rodzaju dziesiętnego.

+1

ostrożny, nie sprawdzić, czy wynik jest prawidłowy. Gdy próbuje dodać '11m', jeśli' 10m' zostanie dodana zamiast, ta metoda nie wyjątek. '(Wynik - a = b || wynik -! B = a)' powinna obejmować, że jeśli nie będę widokiem jakieś szczególne przypadki. – hvd

+0

@hvd, dobry punkt! Naprawiłem to. –

+0

To miłe, że ktoś może nawet utrzymać wydajności wpływ na rozsądnym poziomie. – Andreas

0

Można zrobić klasę SaveDecimal i przeciążać operatora +
https://msdn.microsoft.com/en-us/library/aa288467%28v=vs.71%29.aspx

public class SafeDecimal 
{ 
    private decimal DecValue; 

    public SafeDecimal(decimal Value) 
    { 
     DecValue = Value; 
    } 

    public decimal GetValue() 
    { 
     return DecValue; 
    } 

    public static SafeDecimal operator +(SafeDecimal A, SafeDecimal B) 
    { 
     decimal almostMax = Decimal.MaxValue - 1; 

     checked 
     { 
      if (almostMax <= A.GetValue() + B.GetValue()) 
       throw new Exception("----scary error message----"); 
     } 

     return new SafeDecimal(A.GetValue() + B.GetValue()); 
    } 
} 
+1

Czy uwzględnia wartości ujemne? – zerkms

+0

Nie, nie ma. Prawdopodobnie powinienem to naprawić, dziękuję. –

+1

C# oferuje już ochronę przed przepełnieniem dla działań arytmetycznych (zobacz przykład: http://csharppad.com/gist/7f4edd08bde1061201ca13dca9332806). Ponownie wdrażasz coś, co już istnieje, a pytanie dotyczyło precyzji - nie przepełnia się. Ogólna idea rozwiązania wydaje się jednak dobra. Po prostu zmień kod tak, aby '+ 'generował wyjątek w warunkach określonych przez OP i ta odpowiedź jest dobra. –

Powiązane problemy