Błędne zaokrąglanie części dziesiętnych?

0

Uderzyło mnie, że w zależności od tego jakiej metody użyjemy w danym języku do zaokrąglania części dziesiętnych dwóch liczb, tj. 2.35 oraz 2.45, to w większości przypadków otrzymamy... błędne wyniki. Błędne, bo niezgodne z zasadą:

"Jeśli pierwsza (licząc od lewej strony) z odrzuconych cyfr jest równa 5 i nie następuje po niej żadna cyfra inna niż zero, to ostatnią pozostawioną cyfrę powiększa się o jednostkę, jeśli jest to cyfra nieparzysta."

Python:

# Wynik poprawny.
print(np.around(2.35, decimals=1), np.around(2.45, decimals=1))
# Out: 2.4 2.4
# Wynik błędny.
print(round(2.35, 1), round(2.45, 1))
# Out: 2.4 2.5
# Wynik błędny.
print("%.1f" % 2.35, "%.1f" % 2.45)
# Out: 2.4 2.5

Java:

// Wynik błędny.
System.out.println((double) Math.round(2.35 * 10) / 10 + " " + (double) Math.round(2.45 * 10) / 10);
// Out: 2.4 2.5
// Wynik błędny.
System.out.println(String.format("%.1f", 2.35) + " " + String.format("%.1f", 2.45));
// Out: 2,4 2,5
// Wynik błędny.
DecimalFormat df = new DecimalFormat("#.#");
System.out.println(df.format(2.35) + " " + df.format(2.45));
// Out: 2,4 2,5
// Wynik błędny.
public static void main(String[] args) {
    System.out.println(round(2.35, 1) + " " + round(2.45, 1));
}

public static double round(double value, int places) {
    if (places < 0) throw new IllegalArgumentException();
    BigDecimal bd = new BigDecimal(value);
    bd = bd.setScale(places, RoundingMode.HALF_UP);
    return bd.doubleValue();
}
// Out: 2.4 2.5
// Dla innych RoundingMode, np. HALF_EVEN, wynik również błędny.
// Wynik błędny.
import org.apache.commons.math3.util.Precision;
System.out.println(Precision.round(2.35, 1) + " " + Precision.round(2.45, 1));
// Out: 2.4 2.5

W Matlabie, JS'ie i C# nie udało mi się znaleźć metody, która zwracałaby wartości poprawne.

EDIT

Jeszcze taka ciekawostka:

# Python.
In[26]: x = 2.35
In[27]: y = 2.45
In[28]: print("%.20f, %.20f" % (x, y))
Out [28]: 2.35000000000000008882, 2.45000000000000017764
// Java.
System.out.println(String.format("%.20f", 2.35) + " " + String.format("%.20f", 2.45));
// Out: 2,35000000000000000000 2,45000000000000000000
2

Nie jest to błędne.

Metod zaokrąglania jest po prostu kilka. Tak samo, jednostek miar jest kilka.

Kwestia która została zaimplementowana. Ta którą Ty opisujesz, mnie osobiście wydaje się być... najsprawiedliwsza.

Ale często spotykana jest "prostsza": od 5-tki zaokrąglamy "w górę", czyli w stronę większej wartości względnej (co ma znaczenie dla liczb ujemnych). Ta jak widać jest właśnie zaimplementowana, i nic dziwnego, bo jest w powszechnym użyciu.

0

Bodajże C# stosuje zaokrąglanie do najbliższej liczby parzystej. Nie ma reguły, tak samo nie ma jednej zasady działania operatora modulo dla liczb ujemnych.

1

@Gjorni: lol, pierwszy raz widzę kogoś kto jest przekonany o tym że metoda z parzystością jest poprawna :)
Mieszkasz w Stanach albo używasz Delphi?

teoretycznie: https://www.mathsisfun.com/numbers/rounding-methods.html
praktycznie: http://www.merlyn.demon.co.uk/pas-chop.htm

1

a jak zaczniesz coś wysyłać na drukarkę fiskalną to dopiero będziesz miał jaja z zaokrąglaniem :p

0
abrakadaber napisał(a):

a jak zaczniesz coś wysyłać na drukarkę fiskalną to dopiero będziesz miał jaja z zaokrąglaniem :p

Ja miałem pół dnia roboty - przez to zaokrąglanie w Delphi spora gromadka ludzi szukała 6 groszy w oprogramowaniu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1