2012-05-17 8 views
7

Próbowałem znaleźć bardzo dziwne zachowanie w Javie. Mam formułę, która wymaga podwójnego, ale jest "gwarantowana", aby dać całkowitą odpowiedź - konkretnie, niepodpisaną 32-bitową liczbę całkowitą (która, niestety, Java nie działa dobrze). Niestety, moje odpowiedzi były czasami niepoprawne.Czy prymitywne rzuty Java typu "liczba całkowita" są "ograniczone" w MAX_INT odlewania?

końcu znalazłem problem, ale zachowanie jest nadal bardzo dziwne dla mnie: a double odlewane bezpośrednio do int wydaje się być ograniczona w MAX_INT za podpisanym całkowitej, natomiast double obsady do long że to jest następnie rzutowany na int daje mi oczekiwaną odpowiedź (-1; MAX INT z unsigned 32-bitowej liczby całkowitej reprezentowanej jako 32-bitowa liczba całkowita ze znakiem).

napisałem mały program testowy:

public static void main(String[] args) { 
    // This is the Max Int for a 32-bit unsigned integer 
    double maxUIntAsDouble = 4294967295.00; 
    long maxUintFromDoubleAsLong = (long)maxUIntAsDouble; 
    long maxUintFromDoubleAsInt = (int)maxUIntAsDouble; 
    int formulaTest = (int) (maxUintFromDoubleAsLong * 1.0); 
    int testFormulaeWithDoubleCast = (int)((long) (maxUintFromDoubleAsLong * 1.0)); 
    // This is a more-or-less random "big number" 
    long longUnderTest = 4123456789L; 
    // Max int for a 32-bit unsigned integer 
    long longUnderTest2 = 4294967295L; 
    int intFromLong = (int) longUnderTest; 
    int intFromLong2 = (int) longUnderTest2; 
    System.out.println("Long is: " + longUnderTest); 
    System.out.println("Translated to Int is:" + intFromLong); 
    System.out.println("Long 2 is: " + longUnderTest2); 
    System.out.println("Translated to Int is:" + intFromLong2); 
    System.out.println("Max UInt as Double: " + maxUIntAsDouble); 
    System.out.println("Max UInt from Double to Long: " + maxUintFromDoubleAsLong); 
    System.out.println("Max UInt from Double to Int: " + maxUintFromDoubleAsInt); 
    System.out.println("Formula test: " + formulaTest); 
    System.out.println("Formula Test with Double Cast: " + testFormulaeWithDoubleCast); 
} 

Gdy ten mały program uzyskać:

Long is: 4123456789 
Translated to Int is:-171510507 
Long 2 is: 4294967295 
Translated to Int is:-1 
Max UInt as Double: 4.294967295E9 
Max UInt from Double to Long: 4294967295 
Max UInt from Double to Int: 2147483647 
// MAX INT for an unsigned int 
Formula test: 2147483647 
// Binary: all 1s, which is what I expected 
Formula Test with Double Cast: -1 

Dolne dwie linie są te Próbuję zrozumieć. Podwójna obsada daje mi oczekiwaną wartość "-1"; ale prosta obsada daje mi MAX_INT dla 32-bitowej liczby całkowitej ze znakiem. Pochodzę z tła w C++, zrozumiałbym, gdyby dał mi "nieparzystą liczbę" zamiast oczekiwanego -1 (znanego też jako "naiwne rzucanie"), ale to mnie wprawiło w zakłopotanie.

Tak, na pytanie to: czy to „oczekuje” zachowania w Javie (np dowolny double odlewane bezpośrednio do int będzie „ograniczona” do MAX_INT)? Czy rzutowanie to robi dla nieoczekiwanych typów? Spodziewam się, że będzie podobny na przykład do short i byte; ale jakie jest "oczekiwane zachowanie" podczas rzucania oversized-double do float?

Dzięki!

Odpowiedz

10

Jest to oczekiwane zachowanie. Pamiętaj, że nie ma żadnych pierwotnych typów unsigned long lub int w Javie i Java Language Specification (Java 7) dla zawężania prymitywnej konwersji (5.1.3) stwierdza, że ​​rzucanie "zbyt małej lub zbyt dużej" wartości zmiennoprzecinkowej (podwójnej lub zmiennoprzecinkowej) do integralnego typu int lub długiej użyje minimalnej lub maksymalnej wartości podpisanych całek (podkreślenie moje):

Przewężenie konwersja liczby zmiennoprzecinkowej integralną typu T wykonuje dwa etapy:

  1. W pierwszym etapie, ilość zmiennoprzecinkową przetwarzana jest długi, jeżeli T długi, lub do int, jeśli T jest bajt, krótki, char, lub int, jak następuje:

    • Jeśli liczba zmiennoprzecinkowa jest NaN (§4.2.3), wynik z pierwszego etapu konwersji jest int lub długo 0.
    • W przeciwnym razie, jeżeli liczba zmiennoprzecinkowa nie jest nieskończoność, wartość zmiennoprzecinkowa jest zaokrąglana do liczby całkowitej V, zaokrąglając w kierunku zera, stosując tryb IEEE 754 w obiegu w kierunku zera (§ 4.2.3). Następnie są dwa przypadki:

      • a. Jeśli T jest długie, a ta wartość całkowita może być reprezentowana jako długa, to wynikiem pierwszego kroku jest długa wartość V.
      • b. W przeciwnym razie, jeżeli liczba całkowita może być przedstawiony jako int, a wynik pierwszego etapu int wartość V.
    • inaczej, jednym z następujących przypadków muszą być spełnione:

      • a. Wartość musi być za mała (wartość ujemna dużej wielkości lub ujemnej nieskończoności), a wynikiem pierwszego kroku jest najmniejsza reprezentowalna wartość typu int lub long.
      • b. Wartość musi być zbyt duża (wartość dodatnia dużej wielkości lub dodatniej nieskończoności), a wynikiem pierwszego kroku jest największa reprezentowalna wartość typu int lub long. *
  2. W drugim kroku: * Jeżeli T jest całkowitoliczbową wynik konwersji jest wynikiem pierwszego etapu. * Jeśli T jest bajt, char lub krótki, wynik konwersji jest wynikiem zawężającej konwersji na typ T (§ 5.1.3) wyniku pierwszego kroku.

Przykład 5.1.3-1. Zwężenie prymitywna konwersja

class Test { 
    public static void main(String[] args) { 
     float fmin = Float.NEGATIVE_INFINITY; 
     float fmax = Float.POSITIVE_INFINITY; 
     System.out.println("long: " + (long)fmin + ".." + (long)fmax); 
     System.out.println("int: " + (int)fmin + ".." + (int)fmax); 
     System.out.println("short: " + (short)fmin + ".." + (short)fmax); 
     System.out.println("char: " + (int)(char)fmin + ".." + (int)(char)fmax); 
     System.out.println("byte: " + (byte)fmin + ".." + (byte)fmax); 
    } 
} 

Ten program generuje dane wyjściowe:

long: -9223372036854775808..9223372036854775807 
int: -2147483648..2147483647 
short: 0..-1 
char: 0..65535 
byte: 0..-1 

Wyniki dla char, int, i długo są zaskakujące, dając minimalne i maksymalne wartości reprezentowalna typu.

Wyniki dla bajtu i krótkiego tracą informację o znaku i wartości liczbowej, a także tracą precyzję.Wyniki można zrozumieć, badając bity niskiego rzędu minimum i maksymalne int. Minimalne int jest w systemie szesnastkowym 0x80000000, a maksymalna wartość int to 0x7fffffff. Wyjaśnia to krótkie wyniki, które wynoszą 16 bitów tych wartości, a mianowicie 0x0000 i 0xffff; to wyjaśnia wyniki char, które również są 16 bitami tych wartości , a mianowicie "\ u0000" i "\ uffff"; i objaśnia bajt wyniki, które są niskie 8 bitów tych wartości, a mianowicie 0x00 i 0xff.

Pierwszy przypadek int formulaTest = (int) (maxUintFromDoubleAsLong * 1.0); sprzyja zatem maxUintFromDoubleAsLong do podwójnie przez mnożenie i rzuca do wew. Ponieważ wartość jest za duża, aby reprezentować ją jako liczbę całkowitą ze znakiem, wartość wynosi 2147483647 (Integer.MAX_VALUE) lub 0x7FFFFFFF.

chodzi o ostatnim przypadku:

Konwersja zwężenie liczba całkowita ze znakiem na integralnej typu T po prostu odrzuca wszystkie jednak n najniższe bity rzędu, gdzie n jest liczbą bitów stosowanych do reprezentowania typ T. Poza możliwą utratą informacji o wartości liczbowej, może to spowodować, że znak wartości wynikowej będzie różnił się od znaku wartości wejściowej .

Więc int testFormulaeWithDoubleCast = (int)((long) (maxUintFromDoubleAsLong * 1.0)); pierwszy promuje maxUintFromDoubleAsLong podwoić, z powrotem do dawno (jeszcze montaż), a potem do int. W ostatniej obsadzie nadmiar bitów jest po prostu odrzucany, co daje ci wartość 0xFFFFFFFF, która wynosi -1, gdy interpretowana jest jako liczba całkowita ze znakiem.

+0

Wiedziałem o braku "unsigned" w java (jedna z rzeczy, która sprawia, że ​​Java sprawia ból podczas pracy z bitowymi protokołami), ale większość z nich jest zupełnie nowa. Na pewno nie spodziewałbym się tego zachowania z "krótkiego" i "bajtowego". Dzięki. :RE –

3

To jest sposób w jaki napisana jest specyfikacja języka. Konwertując zmiennoprzecinkowe na typ całkowity, jeśli wartość jest zbyt duża dla miejsca docelowego, wówczas zastępowana jest wartość maksymalna. W zwężającej się konwersji z jednej liczby całkowitej na mniejszą, bity wyższego rzędu są odrzucane.

Zobacz JLS 5.1.3. Narrowing Primitive Conversion

Więc odpowiedź na pytanie w tytule brzmi „tak”.