Zaokrąglanie

Po ostatnich poprawkach w kodzie procedur wyliczających ile kasy należy komu wypłacić odbiorca zwrócił uwagę, że w jego przypadku wychodzą inne wyniki przy zaokrąglaniu do uzgodnionej liczby miejsc po przecinku. Testowanie wyników odbywało się poprzez Accessa i procedury w VBA, co powodowało rozjazd z danymi SQL Servera. Cóż, zaokrąglenia mogą być różne, nie tylko znane wszem i wobec arytmetyczne. Źle tylko, że tak samo nazywająca się funkcja ma różne implementacje w różnych językach – trzeba sprawdzać która z nich akurat jest przyjęta.

Krótko o podstawowych zaokrągleniach:

  • arytmetyczne: [0-4] w dół, [5-9] w górę: 1.4 = 1, 1.5 = 2
  • do parzystej: liczba zaokrąglana zawsze do najbliższej parzystej: 1.5 = 2, 2.5 = 2
  • w dół: nie bierze się pod uwagę tego co po przecinku: 1.4 = 1
  • w górę: jeśli liczba po przecinku > 0, to dodaj 1: 1.4 = 2

Sam algorytm zaokrąglania do parzystej ma sens przy operacjach finansowych. W przypadku gdy zawsze 0.5 zaokrąglamy w górę, a 0.4 w dół możemy mieć dużą rozbieżność w ostatecznych wynikach. Zaokrąglanie do parzystej (nazywane też banker’s rounding) pozwala ten błąd zniwelować.

SQL Server i jego funkcja ROUND() zaokrągla metodą arytmetyczną. VBA zaokrągla metodą banker’s rounding. Przykłady jak można zaimplementować poszczególne algorytmy zaokrąglania w VBA i T-SQL znajdziecie w linkach poniżej. Na szybko dwie procedury z linków:

zaokrąglanie arytmetyczne w VBA: [Microsoft]

Function SymArith(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
     SymArith = Fix(X * Factor + 0.5 * Sgn(X)) / Factor
   '  Alternately:
   '  SymArith = Abs(AsymArith(X, Factor)) * Sgn(X)
   End Function

banker’s rounding w T-SQL: [less than dot]

CREATE FUNCTION dbo.BankersRound(@Val DECIMAL(32,16), @Digits INT)
RETURNS DECIMAL(32,16)
AS
BEGIN
    RETURN CASE WHEN ABS(@Val - ROUND(@Val, @Digits, 1)) * POWER(10, @Digits+1) = 5
                THEN ROUND(@Val, @Digits, CASE WHEN CONVERT(INT, ROUND(ABS(@Val) * POWER(10,@Digits), 0, 1)) % 2 = 1 THEN 0 ELSE 1 END)
                ELSE ROUND(@Val, @Digits)
                END
END

Gdzie można więcej o tym poczytać:
Rounding Algorithms 101
SQL Server Rounding Methods
How To Implement Custom Rounding Procedures
Access – Rounding Functions (nearest decimal / nearest multiple)
Zaokrąglanie – excelowe dziwactwa i niekonsekwencje

Advertisements

2 uwagi do wpisu “Zaokrąglanie

  1. „arytmetyczne: [0-4] w dół, [5-9] w górę: 1.4 = 1, 1.5 =1”

    Przy 1.5 zaokrąglenie arytmetyczne zwiększa wartość zmiennej do 2.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s