8

私はいくつかの非常に奇妙なJavaの振る舞いを追跡しようとしていました。私はdoubleを含む式を持っていますが、整数の答えを与えることが「保証」されています。具体的には、符号なし32ビット整数です(残念ながら、Javaはうまく機能しません)。残念ながら、私の答えは時々間違っていました。

最終的に問題を見つけましたが、動作はまだ私には非常に奇妙です。aにdouble直接キャストすると、符号付き整数intの場合は上限があるように見えますが、aキャストすると、期待される答えが得られます( -1;符号付き32ビット整数として表される符号なし32ビット整数のMAXINT)。MAX_INTdoublelongint

私は小さなテストプログラムを書きました:

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);
}

この小さなプログラムを実行すると、次のようになります。

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

下の2行は、私が理解しようとしている行です。ダブルキャストは私に期待される「-1」を与えます。しかし、ストレートキャストでは、32ビットの符号付き整数のMAX_INTが得られます。C ++のバックグラウンドから来ているので、予想される-1(別名「ナイーブキャスティング」)ではなく「奇数」が表示されるかどうかはわかりますが、これには戸惑います。

では、質問に対して、Javaでのこの「予想される」動作はありますか(たとえば、doubleに直接キャストすると、intに「上限」が設定されMAX_INTます)?キャストは予期しないタイプに対してこれを行いますか?たとえば、shortとについても同様であると思います。byteしかし、特大のダブルをフロートにキャストするときの「予想される動作」は何ですか?

ありがとう!

4

2 に答える 2

11

これは予期される動作です。Java にはプリミティブな unsigned long 型または int 型がなく、 Narrowing プリミティブ変換 (5.1.3) に関するJava 言語仕様(Java 7) には、「小さすぎるか大きすぎる」浮動小数点値 (double であっても) をキャストすることが記載されていることに注意してください。または float) を int または long の整数型に変換すると、符号付き整数型の最小値または最大値が使用されます (強調鉱山):

浮動小数点数から整数型 T への縮小変換には、次の 2 つの手順が必要です。

  1. 最初のステップで、浮動小数点数は、次のように、T が long の場合は long に、T が byte、short、char、または int の場合は int に変換されます。

    • 浮動小数点数が NaN の場合 (§4.2.3)、変換の最初のステップの結果は int または long 0 です。
    • それ以外の場合、浮動小数点数が無限大でない場合、浮動小数点値は整数値 V に丸められ、IEEE 754 ゼロ方向への丸めモード (§4.2.3) を使用してゼロ方向に丸められます。次に、2 つのケースがあります。

      • を。T が long で、この整数値が long として表現できる場合、最初のステップの結果は long 値 V になります。
      • b. それ以外の場合、この整数値が int として表現できる場合、最初のステップの結果は int 値 V になります。
    • それ以外の場合は、次の 2 つのケースのいずれかに該当する必要があります。

      • を。値は小さすぎる必要があり (大きさが大きい負の値または負の無限大)、最初のステップの結果は int または long 型の表現可能な最小値になります。
      • b. 値は大きすぎる必要があり (大きさが大きい正の値または正の無限大)、最初のステップの結果は int または long 型の表現可能な最大値になります。*
  2. 2 番目のステップ: * T が int または long の場合、変換の結果は最初のステップの結果になります。* T が byte、char、または short の場合、変換の結果は、最初のステップの結果の型 T (§5.1.3) への縮小変換の結果です。

例 5.1.3-1。プリミティブ変換のナローイング

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);
    }
}

このプログラムは次の出力を生成します。

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

char、int、および long の結果は驚くことではなく、型の表現可能な最小値と最大値を生成します。

byte と short の結果では、数値の符号と大きさに関する情報が失われ、精度も失われます。結果は、int の最小値と最大値の下位ビットを調べることで理解できます。最小の int は 16 進数で 0x80000000 であり、最大の int は 0x7fffffff です。これは、これらの値の下位 16 ビット、つまり 0x0000 と 0xffff である短い結果を説明しています。これは、これらの値の下位 16 ビット、つまり「\u0000」と「\uffff」でもある char の結果を説明しています。これらの値の下位 8 ビット、つまり 0x00 と 0xff であるバイト結果を説明します。

したがって、最初のケースは、乗算を介して doubleint formulaTest = (int) (maxUintFromDoubleAsLong * 1.0);に昇格maxUintFromDoubleAsLongし、それを int にキャストします。符号付き整数として表すには値が大きすぎるため、値は 2147483647 (Integer.MAX_VALUE) または 0x7FFFFFFF になります。

後者の場合については:

符号付き整数から整数型 T への縮小変換では、下位 n ビットを除くすべてのビットが単純に破棄されます。ここで、n は型 T を表すために使用されるビット数です。数値の大きさに関する情報が失われる可能性に加えて、 、これにより、結果の値の符号が入力値の符号と異なる場合があります。

そのため、最初にmaxUintFromDoubleAsLongint testFormulaeWithDoubleCast = (int)((long) (maxUintFromDoubleAsLong * 1.0));を double に昇格させ、long に戻し (まだ適合)、次に int に昇格させます。最後のキャストでは、余分なビットは単純に削除され、符号付き整数として解釈されると -1 である 0xFFFFFFFF が残ります。

于 2012-05-17T19:12:55.113 に答える
3

これは、言語仕様が書かれている方法です。浮動小数点を整数型に変換すると、値が変換先に対して大きすぎる場合、最大値が代用されます。ある整数型からより小さな整数型への縮小変換では、上位ビットが破棄されます。

JLS 5.1.3 を参照してください。プリミティブ変換のナローイング

したがって、タイトルの質問に対する答えは「はい」です。

于 2012-05-17T19:12:03.913 に答える