Java i matematyka

0

Cześć!

Jestem zupełnie zielony w temacie Javy, stawiam pierwsze kroki. W ramach ćwiczeń napisałem prościutki program który oblicza kwotę netto na podstawie kwoty brutto i stawki VAT, czyli prosta matematyka.

Oto kod:

public class b2n {

	public static void main(String[] args) {
		double brutto = 1070.0;
		double netto;
		double vat = 7.0;
		
		netto = brutto/(1+vat/100);
		System.out.println("Kwota netto wynosi: " + netto);		
	}
	
}

Na mój mały rozumek wynik powinien wyjść 1000.0, ale Java odpowiada, że 999.9999999999999.
Kiedy zmieniłem wartość brutto na 107.0 - wyszło OK (czyli 100.0), kiedy zmieniłem wartość brutto na 10700.0 - wyszło OK (czyli 10000.0) a przy 1070.0 wywala mi się. I nie wiem dlaczego.

Z góry dzięki za odpowiedź!

0

Błędy zaokrągleń floatów/double.

Kwoty i tak się zaokrągla do 2 miejsc po przecinku, więc po zaokrągleniu będziesz mieć poprawny wynik.

0

do dokładnych obliczeń finansowych używaj klas BigDecimal i BigInteger

0
nav napisał(a)

Błędy zaokrągleń floatów/double.

Kwoty i tak się zaokrągla do 2 miejsc po przecinku, więc po zaokrągleniu będziesz mieć poprawny wynik.

NAV, ale to są moje błędy czy Javy?

0

@salvi, teorii budowy komputerów po prostu maszyna nie potrafi rozwinąć liczby w nieskończoność.

Zadania spróbuj dodać na kartce 1/3 i 1/3 i 1/3, a następnie w ten sposób:
int i = 1/3 + 1/3 + 1/3;
wyjdzie 0 :)bo komp zaokrągli 1/3 do 0.33333333333 a ty zaokrąglasz do 0,3(3) czyli nie gubisz cyfr po przecinku.

0
Koziołek napisał(a)

do dokładnych obliczeń finansowych używaj klas BigDecimal i BigInteger

Koziołku, help! Albo nie wiem jak użyć tych klas albo nie wiem co... Zaimportowałem java.Math.* a kompilator mi mówi, że:

Type mismatch: cannot convert from int to BigInteger
	Type mismatch: cannot convert from int to BigInteger
	The operator / is undefined for the argument type(s) BigInteger, int
0

BigDecimal i BigInteger mają metody odpowiedzialne za operacje matematyczne.
POza tym poczytaj dokumentacje. Akurat ta Javowa jest bardzo dobrze zorganizowana:
http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html

0
Koziołek napisał(a)

@salvi, teorii budowy komputerów po prostu maszyna nie potrafi rozwinąć liczby w nieskończoność.

Zadania spróbuj dodać na kartce 1/3 i 1/3 i 1/3, a następnie w ten sposób:
int i = 1/3 + 1/3 + 1/3;
wyjdzie 0 :)bo komp zaokrągli 1/3 do 0.33333333333 a ty zaokrąglasz do 0,3(3) czyli nie gubisz cyfr po przecinku.

No tak, ale przytoczone przez mnie przykłady operują na skończonych liczbach - PHP dobrze je liczy, nie wspominając o Windowsowym kalkulatorze a nawet chińskim kieszonkowym ;-) .

Poza tym spróbuj tego:

<?php echo 1/3 + 1/3 + 1/3; ?>

Wychodzi dokładnie 1 (słownie: jeden)

0

Bo w PHP są używane floaty. Jeśli zamiast int użyjesz float/double też wyjdzie 1.

0

Nie masz racji, że działasz na liczbach "skończonych" (rozumiem że ten zwrot oznacza liczby o skończonym rozwinięciu). Liczba 7.0/100 ma skończone rozwinięcie dziesiętne ale nieskończone dwójkowe.
Komputer korzysta z dwójkowego.
Różnica między 1000.000 a 999.99999.. nie jest szokująca.
Polecam obliczenia pola trójkąta ze wzoru Herona. Dla trójkąta o bokach 12345678.0 123456789/0 oraz
1.01233995 wynik (w Javie) wynosi 0 dla typu float oraz 972730,0557076166 dla typu double.
Pozdrawiam

0

bogdans,

masz rację, miałem na myśli skończone rozwinięcie dziesiętne i zapomniałem o tym, że komputer tak nie działa - to efekt uboczny PHP, w którym można o tym zapomnieć i takie obliczenia jak moje wykonywać jak na kalkulatorze.

Czyli faktycznie, w Javie nie ma wyjścia innego niż stosowanie klasy BigDecimal. Tyle, że w moim odczuciu jest to bardzo niewygodne rozwiązanie. Moje zadanie polegające na obliczaniu kwoty netto na podstwie kwoty brutto i stawki VAT rozpisałem tak:

    	BigDecimal brutto = new BigDecimal("1070.00");
    	BigDecimal netto = new BigDecimal("0");
    	BigDecimal vat = new BigDecimal("0.07");
    	BigDecimal vat2 = new BigDecimal("0");
    	BigDecimal jeden = new BigDecimal("1");
    	vat2 = vat.add(jeden);
    	
    	netto = brutto.divide(vat2);
    	System.out.println(netto);

No i teraz działa - wynik jest "1000". Ale to co mnie tu boli to to, że muszę deklarować zmienne "pomocnicze" jak np. vat2 (bo nie mogę użyć jako argumentu zapisu (1+vat)), której np. w PHP nie musiałbym deklarować bo użyłbym po prostu wzoru:

$netto = $brutto/(1+$vat);

Strach pomyśleć co się będzie działo przy bardziej złożonych wzorach. Chyba, że znowu o czymś nie wie (co jest bardzo prawdopodobne). Jeśli tak, to proszę o kaganek oświaty.

0
bogdans napisał(a)

Różnica między 1000.000 a 999.99999.. nie jest szokująca.

w wyliczeniach finansowych jest. Ostatnio ojciec miał różnicę 3 groszy na kilku milionach w fakturze w firmie. Efektem czego była zarwana noc i wyszukiwanie błędów zaokrągleń :(

@salvi, php automatycznie konwertuje typy liczbowe i dlatego jest dobrze; co do kodu obiekty anonimowe i zapis x = x +1:

            BigDecimal brutto = new BigDecimal("1070.00");
            BigDecimal netto = new BigDecimal("0");
            BigDecimal vat = new BigDecimal("0.07");
            vat = vat.add(new BigDecimal("1")); //anonim i x = x+1
           
            netto = brutto.divide(vat);
            System.out.println(netto);

mniej kodu i w dodatku masz jedną zmienną mniej, a obiekt anonimowy jest usuwny zaraz po użyciu bo nic na niego nie wskazuje.

0

Zgadza się - różnica jest. Za te 3 grosze różnicy urząd skarbowy lubi wlepić karę - może dać 300 PLN albo i 3000 PLN. Poza tym spróbujcie sobie rozwiązać jakieś zadanko na simplex - nie ma mowy o żadnych zaokrągleniach czy przybliżeniach.

Koziołku, wielkie dzięki! Dużo mi wyjaśniłeś. Choć stosowanie klas BigInteger nieco studzi mój zapał do Javy - trudno, będę się musiał przyzwyczaić... ;-)

0

@salvi, nie ma sprawy, a BigInteger jest uciażliwy, ale można się przyzwyczaić. W javie 7 będzie przeciążanie operatorów zatem odpadną problemy z wywoływaniem metod :)

0

na finansowe jest prosta metoda - dane przechowujesz w minimalnej jednostce (np. grosz) a na warstwie prezentacji formatujesz jak trzeba (wtedy nie tracisz dokladnosci przy obliczeniach), natomiast niestety z innymi musisz sobie radzic inaczej. Taka juz jest arytmetyka zmiennoprzecinkowa :P

pozdrawiam

0
Koziołek napisał(a)
bogdans napisał(a)

Różnica między 1000.000 a 999.99999.. nie jest szokująca.

w wyliczeniach finansowych jest. Ostatnio ojciec miał różnicę 3 groszy na kilku milionach w fakturze w firmie. Efektem czego była zarwana noc i wyszukiwanie błędów zaokrągleń :(

Bez przesady. Akurat grosze się w podatkach zaokrągla, więc o ile te 3 grosze to nie jest zamiana 0,48 na 0,51, wszystko jest w porządku.

0

@Krolik, w podatkach mniej boli w fakturach bardziej bo te liczone są co do grosza i dopiero z nich jest liczony podatek i zaokrąglany. W momencie gdy faktury są wystawione w różnych walutach to mogą pojawiać się głupoty typu "3 grosze". Istotnym elementem obliczeń finansowych jest właśnie zaokrąglenie, ale PO policzeniu kwoty. Wyobraźmy sobie sytuację w której pan Kowalski zarabia pewną kwotę N w walucie innej niż PLN. Jeżeli teraz po każdym miesiącu będziemy przeliczać pensję pana Kowalskiego I zaokrąglać to otrzymamy kwotę 12 * (FLOOR N) co w pewnych specyficznych przypadkach może oznaczać np. przejście do niższego progu podatkowego. Zabraknie kilku groszy! Dlatego też we wszystkich obliczeniach najpierw sumujesz a dopiero na samym końcu zaokrąglasz.
Z innej działki jednym z najpopularniejszych błędów na pracowni wstępnej na moim wydziale jest właśnie nieodpowiednia kolejność prowadzenia obliczeń i zaokrągleń. uproszczony przykład, obliczanie średniej wartości:

// niech:
x = 1.5
y = 2.5
 // jeżeli najpierw zaokrąglimy to średnia:
s = (FLOOR (x) + FLOOR (y))/2 = (1 + 2)/2 = 1.5 
// co znowu zaokrąglamy by uzyskać prawidłowy format wyniku:
s = 1

// jeżeli policzymy średnią bez zakrągleń i zaokrąglimy wynik:
s = (x + y)/2 = (1.5 +2.5)/2 = 2.0
s = 2

mała różnica (o ile dwukrotna różnica może być mała) jednak w niektórych przypadkach pomiędzy 1 i 2 to różnica typu mam nobla i nie mam nobla :)

Zasada jest jedna najpierw liczymy jak leci, a dopiero wynik końcowy przedstawiamy w odpowiednim formacie.

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