2010-10-10 10 views
7

Czy coś jest zepsute, czy nie rozumiem, co się dzieje?Jak zmienić dwukrotnie o najmniejszy przyrost

static String getRealBinary(double val) { 
    long tmp = Double.doubleToLongBits(val); 
    StringBuilder sb = new StringBuilder(); 

    for (long n = 64; --n > 0; tmp >>= 1) 
     if ((tmp & 1) == 0) 
      sb.insert(0, ('0')); 
     else 
      sb.insert(0, ('1')); 

    sb.insert(0, '[').insert(2, "] [").insert(16, "] [").append(']'); 
    return sb.toString(); 
} 

public static void main(String[] argv) { 
    for (int j = 3; --j >= 0;) { 
     double d = j; 
     for (int i = 3; --i >= 0;) { 
      d += Double.MIN_VALUE; 
      System.out.println(d +getRealBinary(d)); 
     } 
    } 
} 

Z wyjściem:

2.0[1] [00000000000] [000000000000000000000000000000000000000000000000000] 
2.0[1] [00000000000] [000000000000000000000000000000000000000000000000000] 
2.0[1] [00000000000] [000000000000000000000000000000000000000000000000000] 
1.0[0] [11111111110] [000000000000000000000000000000000000000000000000000] 
1.0[0] [11111111110] [000000000000000000000000000000000000000000000000000] 
1.0[0] [11111111110] [000000000000000000000000000000000000000000000000000] 
4.9E-324[0] [00000000000] [000000000000000000000000000000000000000000000000001] 
1.0E-323[0] [00000000000] [000000000000000000000000000000000000000000000000010] 
1.5E-323[0] [00000000000] [000000000000000000000000000000000000000000000000011] 
+4

Co próbujesz zrobić? Jakie jest Twoje pytanie? – Sjoerd

+0

Moje pytanie brzmi: "Jak zmienić podwójnie przez jego najmniejszy przyrost", a to jest mój wysiłek, który się nie powiódł. – Margus

+1

dlaczego nie po prostu edytować te bity, jeśli chcesz zrobić najmniejszy przyrost, to nie dla 1 i 2, ponieważ MIN_VALUE jest małe (naprawdę małe), więc 0 + naprawdę małe = naprawdę małe, ale 2+ naprawdę małe ~ = 2 to z powodu FLOATING punkt min_value jest z punktem tak daleko w lewo, jak to możliwe, podczas gdy dla dwóch jest gdzieś pośrodku, lewy traci. możesz zobaczyć jego około 300 cyfr po kropce, którą powinna być różnica, w której podwójne magazyny zawierają tylko około 15-20 znaczących cyfr. – flownt

Odpowiedz

8

Ogólna idea jest najpierw przekonwertować podwójne do swojej długiej reprezentacji (używając doubleToLongBits jak masz zrobić w getRealBinary), przyrost o 1 tak długo, aż w końcu przekształcić nowe długi powrót do podwójnego reprezentuje poprzez longBitsToDouble.

EDYTOWANIE: Java (od 1.5) dostarcza Math.ulp(double), które domyślam się możesz użyć do obliczenia następnej wyższej wartości bezpośrednio tak: x + Math.ulp(x).

+2

Od wersji Java 1.6 istnieje ['Math.nextAfter (start, direction)'] (http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#nextAfter (double, % 20double)), który jest o wiele bardziej niezawodny. Obsługuje nawet specjalne przypadki, takie jak około zera. – z0r

7

Liczby zmiennoprzecinkowe nie są rozłożone równomiernie na osi liczbowej jak całkowite typy. Są gęsto upakowane w pobliżu 0 i bardzo daleko od siebie, gdy zbliżasz się do nieskończoności. Dlatego nie ma stałej wartości, którą można dodać do liczby zmiennoprzecinkowej, aby uzyskać kolejny numer zmiennoprzecinkowy.

+0

Jak to możliwe? Mamy mantysę i wykładnik. I nie faworyzuje 0. –

+0

@TonyEnnis: Wykładnik jest po prostu - wykładnik. Przesunięty nieco, wartość liczby zmiennoprzecinkowej jest podobna do 'mantissa * 2^wykładnik'. Oznacza to, że ilość, o którą zmiana mantysy zmienia wynikową liczbę, zależy całkowicie od wartości wykładnika. Im niższy wykładnik, tym mniejsza będzie zmiana. – cHao

4

Twój kod nie jest dobrze sformułowany. Próbujesz dodać minimalną podwójną wartość i oczekujesz, że wynik będzie różny od pierwotnej wartości. Problem polega na tym, że double.MinValue jest tak mały, że wynik jest zaokrąglony i nie ma wpływu.

Zalecane lektury: http://en.wikipedia.org/wiki/Machine_epsilon

Z Wikipedii jest kod Java zbyt. Epsilon jest z definicji najmniejszą liczbą, taką jak (X + eps * X! = X), a eps * X jest nazywany "względnie epsilonem".

0

Jeśli chcesz użyć klasy BigDecimal, istnieje metoda BigDecimal.ulp() także.

1

Od wersji 1.8 programu Java java.lang.Math.nextUp(double) robi dokładnie to, co chcesz. Istnieje również przeciwieństwo java.lang.Math.nextDown(double).