Konkurs matematyczny: Java vs Oracle


Matematyka to królowa nauk i nie mam wątpliwości, że czasem może sprawiać problemy. Niemniej luźne podejście Javy do arytmetyki mnie autentycznie zaskoczyło  :o 

No to do rzeczy. Postawiłam przed Javą oraz przed PL/SQL'em dwa proste zadania

1. ile to jest 3 * 1,2 ?
2. ile to jest 3 * 3000000000 (oracle) i 3 * 1 000 000 000  (java)  (milion dla Oracle to za mało, by się zmęczył a 3 mln dla Java to za dużo i się wywala, dlatego w tym przypadku zadanie się nieco różni)

Ciekawa byłam jak sobie poradzą różne typy danych.

Runda pierwsza: output
Na pierwszy ogień nie ustawiałam żadnych typów danych tylko działanie wykonałam na standardowym outpucie: dbms_output.put_line dla oracle i System.out.print dla Java.

Jak dla Oracle wyniki były takie jak można było oczekiwać:

3 * 1.2 = 3,6
3 * 3000000000 = 9000000000

tak java już ma pewne wątpliwości

3*1.2 = 3.5999999999999996
3*1 000 000 000 = -1294967296

Rozumiem, że przy niektórych typach danych wykonywane może być zaokrąglenie. Ale tutaj java odstawia coś całkowicie przeciwnego - od-zaokrąglenie? Ciekawostka. 
Drugie zadanie też dla java nie było łatwe, zdaje się że myślała i myślała, że aż się licznik przekręcił :)

Runda druga - integer: integer i pls_integer (oracle), int java
Oracle dalej bezbłędny, wynik z przecinkiem zaokrąglił matematycznie w obu przypadkach do całości i wyszło

3 * 1.2 = 4

Duże liczby dalej się liczą poprawnie w przypadku integer

3 * 3000000000 = 90000000000000000

wynik jednak w pls_integer się nie zmieścił. Ale Oracle zamiast kręcić licznikiem to po prostu oznajmił wszem i wobec NIE DA SIĘ.

3 * 3000000000 = ORA-01426: numeric overflow

Java  natomiast dalej chyba bardziej strzela niż liczy. W pierwszym równaniu zamiast zaokrąglenia dostaliśmy proste obcięcie części dziesiętnych

3*1.2 = 3

co do dużych liczb, to dalej się kręcimy bez zahamowań

3*1 000 000 000 = -1294967296

Runda trzecia: long i double (oracle) vs double (java)
O Oracle szybko wspomnimy, że i tym razem dostaliśmy wynik poprawny dla obu typów danych.

3 * 1.2 = 3,6
3 * 3000000000 = 90000000000000000

Java ciągle obstaje przy swoim wyniku

3*1.2 = 3.5999999999999996
3*1 000 000 000 = -1.294967296E9

Runda czwarta: float
Oracle bez zmian, ani chwili się nie waha

3 * 1.2 = 3,6
3 * 3000000000 = 90000000000000000

Java w końcu coś zaczyna kumać, że nie bardzo i próbuje jeszcze raz przeliczyć zadanie. Dobrze że próbuje, ale wynik znów nie trafiony, ale blisko, blisko

3*1.2 = 3.6000001
3*1 000 000 000 = -1.2949673E9

Runda poprawkowa; Big Decimal (java)
W rundzie poprawkowej już odpuściliśmy Oraclowi, bo widać że nie chce podać innego wyniku.
Za to java dostała ostatnią szansę -i ją wykorzystała!

3*1.2 = 3.6
3*1 000 000 000 =  3000000000

Będzie chyba promocja z klasy do klasy :)

No, nie wyzłośliwiajmy się więcej, liczy się  przecież uczestnictwo w konkursie! Plusik dla javy.

Zdaję sobie sprawę, że programiści Java zapewne z mlekiem matki wyssali, że do obliczeń tylko BigDecimal, ale dla mnie, nie zagłębiającej się w ten jakże popularny język, było to totalnym zaskoczeniem :)

Ot, jak trzeba uważać! Jest to zawsze nauka, by nie wierzyć w "oczywiste oczywistości" tylko zawsze i wszędzie sprawdzać, testować. Wtedy będziemy odporni na wszelkie niespodzianki :)

Poniżej skrypty.

**************************************************************************
Java
**************************************************************************

 import java.math.*;  
 public class Calc {  
  public static void main(String[] args){  
      System.out.print("\nggg***************************************\n");  
      System.out.print("defaults dd \n");  
   System.out.print("\n" + " 3*1.2 = " + 3*1.2+"\n");  
      System.out.print("\n" + " 3*1 000 000 000 = " + 3* 1000000000 +" \n");  
      System.out.print("\n****************************************\n");  
      System.out.print(" int \n");  
      int wynik_int ;  
      wynik_int = (int) ( 3*1.2 );  
   System.out.print("\n" + " 3*1.2 = " + wynik_int +"\n");  
      wynik_int = 3* 1000000000 ;  
   System.out.print("\n" + " 3*1 000 000 000 = " + wynik_int+" \n");  
      System.out.print("\n****************************************\n");  
      System.out.print("double\n");  
      double wynik;  
      wynik = 3*1.2 ;  
   System.out.print("\n" + " 3*1.2 = " + wynik +"\n");  
      wynik = 3* 1000000000 ;  
      System.out.print("\n" + " 3*1 000 000 000 = " + wynik+" \n");  
      System.out.print("\n****************************************\n");  
      System.out.print("float \n");  
      float wynik_float;  
      wynik_float = 3*1.2f ;  
   System.out.print("\n" + " 3*1.2 = " + wynik_float +"\n");  
      wynik_float = 3* 1000000000 ;  
      System.out.print("\n" + " 3*1 000 000 000 = " + wynik_float+" \n");  
      System.out.print("\n****************************************\n");  
      System.out.print("big decimal \n");  
      BigDecimal a = new BigDecimal("3");  
      BigDecimal b = new BigDecimal("1.2");  
      BigDecimal bigwynik = b.multiply(a);  
   System.out.print("\n" + " 3*1.2 = " + bigwynik +"\n");  
      b = new BigDecimal("1000000000");  
   bigwynik = b.multiply(a);  
      System.out.print("\n" + " 3*1 000 000 000 = " + bigwynik+"\n");  
  }  
 }  


**************************************************************************
Oracle
**************************************************************************


 set serveroutput on  
 declare   
 wynik_int integer;  
 wynik_intp pls_integer;  
 wynik_number number;  
 wynik_long long;  
 wynik_float float(5);  
 wynik_double double precision (5);  
 begin  
 dbms_output.put_line ('defaults');  
 dbms_output.put_line ('3 * 1.2 = ' ||to_char(3 * 1.2));  
 dbms_output.put_line ('3 * 3000000000 = ' || to_char(3 * 3000000000));  
 dbms_output.put_line (chr(13) || 'int');  
 wynik_int := 3 * 1.2;  
 dbms_output.put_line ('3 * 1.2 = ' ||to_char(wynik_int));  
 wynik_int := 30000000 * 3000000000;  
 dbms_output.put_line ('3 * 3000000000 = ' || to_char(wynik_int));  
 begin  
   dbms_output.put_line (chr(13) || 'pls_integer');  
   wynik_intp := 3 * 1.2;  
   dbms_output.put_line ('3 * 1.2 = ' ||to_char(wynik_intp));  
 exception when others then   
      dbms_output.put_line ('3 * 1.2 = ' ||sqlerrm);  
 end ;  
 begin  
   wynik_intp := 30000000 * 3000000000;  
   dbms_output.put_line ('3 * 3000000000 = ' || to_char(wynik_intp));  
 exception when others then   
      dbms_output.put_line ('3 * 3000000000 = ' ||sqlerrm);  
 end ;  
      dbms_output.put_line (chr(13) || 'long');  
      wynik_long := 3 * 1.2;  
      dbms_output.put_line ('3 * 1.2 = ' ||to_char(wynik_long));  
      wynik_long := 30000000 * 3000000000;  
      dbms_output.put_line ('3 * 3000000000 = ' || to_char(wynik_long));  
      dbms_output.put_line (chr(13) || 'float');  
      wynik_float := 3 * 1.2;  
      dbms_output.put_line ('3 * 1.2 = ' ||to_char(wynik_float));  
      wynik_float := 30000000 * 3000000000;  
      dbms_output.put_line ('3 * 3000000000 = ' || to_char(wynik_float));  
      dbms_output.put_line (chr(13) || 'double');  
      wynik_double := 3 * 1.2;  
      dbms_output.put_line ('3 * 1.2 = ' ||to_char(wynik_double));  
      wynik_double := 30000000 * 3000000000;  
      dbms_output.put_line ('3 * 3000000000 = ' || to_char(wynik_double));  
 end;  

Komentarze

  1. Java, podobnie jak większość języków implementuje standard IEEE754. Oracle też je ma, ale jako binary_float/double. Podstawowy float/double jest aliasem na number, który używa BCD jako reprezentacji liczbowej. Więc wszelkie operacje na tych liczbach muszą być wykonywane tak jakby robił to człowiek metodą "pod kreską". Przez to też te pola mają dynamiczne rozmiary (1-22 bajty). W wielu językach typy proste są bezpośrednim przełożeniem na typy, na których procesor umie robić arytmetykę.

    Oracle na typach binary_* zachowa się tak samo jak java (i wiele innych języków). Za to powinna być wyraźna różnica w wydajności operacji arytmetycznych.

    OdpowiedzUsuń
  2. No i proszę, wszystko jasne :) I faktycznie Oracle też czasem zapomina jak liczyć :) Dzięki za objaśnienie zagadki :o

    binary_integer
    3 * 1.2 = 4

    wynik_binary_float
    3 * 1.2 = 3.5999999E+000
    3 * 3000000000 = 8.99999949E+009

    wynik_binary_float
    3 * 1.2 = 3.6000000000000001E+000
    3 * 3000000000 = 9.0E+009

    OdpowiedzUsuń
    Odpowiedzi
    1. Ostatni wynik jest dla binary_double. bład kopiego pasta :P

      Usuń

Prześlij komentarz