Javaがintをバイトに変換するときの奇妙な動作?


130
int i =132;

byte b =(byte)i; System.out.println(b);

マインドボグリング。出力はなぜ-124ですか?

回答:


172

Javaでは、int32ビットです。A byteは8 bitsです。

Javaで最も原始的なタイプが署名され、そしてbyteshortint、およびlong2の補数でエンコードされています。(charタイプは符号なしであり、符号の概念はには適用されませんboolean。)

この数値スキームでは、最上位ビットが数値の符号を指定します。さらに多くのビットが必要な場合は、最上位ビット( "MSB")が新しいMSBにコピーされます。

したがって、byte 255:が11111111 あり、それをint(32ビット)として表現する場合は、1を左に24回コピーするだけです。

ここで、負の2の補数を読み取る1つの方法は、最下位ビットから始め、最初の1が見つかるまで左に移動し、その後、すべてのビットを反転することです。結果の数値は、その数値の正のバージョンです

例:11111111go to 00000001= -1。これは、Javaが値として表示するものです。

おそらくあなたがしたいことは、バイトの符号なしの値を知ることです。

これは、最下位の8ビット以外のすべてを削除するビットマスクで実現できます。(0xff)

そう:

byte signedByte = -1;
int unsignedByte = signedByte & (0xff);

System.out.println("Signed: " + signedByte + " Unsigned: " + unsignedByte);

印刷します: "Signed: -1 Unsigned: 255"

ここで実際に何が起こっていますか?

ビットごとのANDを使用して、すべての無関係な符号ビット(最下位8ビットの左側にある1)をマスクしています。intがバイトに変換されると、Javaは左端の24ビットを切り捨てます

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

32番目のビットが8番目のビットではなく符号ビットになったため(符号ビットを正の0に設定)、バイトからの元の8ビットがJavaによって正の値として読み取られます。


1
よくやった、このテーマについての最高の説明、ウェイン!2の補数表現でビットを追加するために右側に符号ビットをコピーできるのは、なぜ数学の定式化を探しているのですか。数の負の値をどのように取得するかというルールを考えると、理解しやすいです。つまり、すべてのビットを右から左に検討し、最初の1が含まれるまで変更せずに書き込みます。次に、後続のビットを反転します。不足しているビットを0と見なすと、すべてが1になることは簡単に理解できます。しかし、より「数学的な」説明を探していました。
AgostinoX

ここsignedByte & (0xff)0xff問題なのは、それが整数リテラルであるため、ビット単位の演算が実行される前に、signedByteが整数に昇格されることです。
Kevin Wheeler

それは0xFFではなく、あなたの例では0x7Eです!
JohnyTex 2018年

89

132数字(base 10)は1000_0100ビット(base 2)であり、Java intは32ビットで格納します。

0000_0000_0000_0000_0000_0000_1000_0100

int-to-byteのアルゴリズムはleft-truncateです。のアルゴリズムSystem.out.println2の補数です2の補数は、左端のビットがの場合1、負の1の補数(ビットの反転)-1として解釈されます)。したがってSystem.out.println(int-to-byte( ))

  • interpret-as(if-leftmost-bit-is-1 [negative(invert-bits(minus-one(] left-truncate(0000_0000_0000_0000_0000_0000_1000_0100)[)))]])
  • = interpret-as(if-leftmost-bit-is-1 [negative(invert-bits(minus-one(] 1000_0100[)))])
  • = interpret-as(negative(invert-bits(minus-one(1000_0100)))))
  • = interpret-as(negative(invert-bits(1000_0011)))
  • = interpret-as(negative(0111_1100))
  • = interpret-as(negative(124))
  • = interpret-as(-124)
  • = -124多田!!!

7
非常にうまく説明された
ZAJ

1
したがって、10進数の132はバイトで-124になります。逆はどのように機能しますか?
Nilesh Deokar、2018年

@NileshDeokar、その逆はPOLAによるものです(JLS 5.1.2を参照)。出力は符号左パッドと一致します(0正および1負の場合)。
Pacerier

POLAとは何ですか?intからaへの変換byteは不可逆変換です(つまり、情報は失われます)。したがって、元のint値に戻す方法はありません。
真理調整者

23

Javaのバイトは署名されているので、範囲は-2 ^ 7から2 ^ 7-1まで、つまり-128から127です。132は127を超えているため、最終的には132-256 = -124に折り返されます。つまり、本質的に256(2 ^ 8)が範囲に入るまで加算または減算されます。

詳細については、2の補数を参照してください


16

132は、-128〜127(Byte.MIN_VALUE〜Byte.MAX_VALUE)のバイトの範囲外です。代わりに、8ビット値の最上位ビットは、この場合は負であることを示す符号付きとして扱われます。したがって、数値は132-256 = -124です。


5

ここでは、注意をそらす理論のない非常に機械的な方法があります。

  1. 数値をバイナリ表現に変換します(計算機を使用しますか?)
  2. 右端の8ビット(LSB)のみをコピーし、残りは破棄します。
  3. 手順2の結果から、左端のビットが0の場合は、計算機を使用して数値を10進数に変換します。これがあなたの答えです。
  4. それ以外の場合(左端のビットが1の場合)、答えは否定的です。右端のすべてのゼロと最初のゼロ以外のビットは変更しません。そして、残りを逆にしました。つまり、1を0に、0を1に置き換えます。次に、計算機を使用して10進数に変換し、値が負であることを示す負の符号を追加します。

このより実用的な方法は、上記の多くの理論的な答えに従っています。したがって、モジュロを使用するように言っているJavaの本をまだ読んでいる人は、上で概説した4つのステップがモジュロ演算ではないので、これは間違いです。


Javaの本で「モジュロ」を使用するように言っているものは何ですか?46年間、Javaの本はもちろんのこと、CSの本も見たことがありません。「モジュロ」って何?Javaにはモジュロ演算はありません。剰余演算子のみ。
ローンの侯爵2017年

grepはもっと難しい。http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdf59ページ
真実調整者

4

2の補数の方程式:

ここに画像の説明を入力してください


Javaでは、byte(N = 8)とint(N = 32)は上記の2の補数で表されます。

方程式から、7はに対して負ですbyteがに対して正ですint

coef:   a7    a6  a5  a4  a3  a2  a1  a0
Binary: 1     0   0   0   0   1   0   0
----------------------------------------------
int:    128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 =  132
byte:  -128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 = -124

2

本では、係数の除算によって実行されるintからbyteへのキャストの説明をよく目にします。以下に示すように、これは厳密には正しくありません。実際に何が起こるかは、int番号のバイナリ値からの最上位24ビットが破棄され、残りの左端のビットが負の数を指定する場合に混乱を残します

public class castingsample{

public static void main(String args[]){

    int i;
    byte y;
    i = 1024;
    for(i = 1024; i > 0; i-- ){

      y = (byte)i;
      System.out.print(i + " mod 128 = " + i%128 + " also ");
      System.out.println(i + " cast to byte " + " = " + y);

    }

}

}

2
46年間、どの本にもそれを見たことがありません。
ローン侯爵

2

動作方法をシミュレートする簡単なアルゴリズムは次のとおりです。

public int toByte(int number) {
    int tmp = number & 0xff
    return (tmp & 0x80) == 0 ? tmp : tmp - 256;
}

これはどのように機能しますか?daixtrの答えを見てください。彼の回答に記載されている正確なアルゴリズムの実装は次のとおりです。

public static int toByte(int number) {
    int tmp = number & 0xff;
    if ((tmp & 0x80) == 0x80) {
        int bit = 1;
        int mask = 0;
        for(;;) {
            mask |= bit;
            if ((tmp & bit) == 0) {
                bit <<=1;
                continue;
            }
            int left = tmp & (~mask);
            int right = tmp & mask;
            left = ~left;
            left &= (~mask);
            tmp = left | right;
            tmp = -(tmp & 0xff);
            break;
        }
    }
    return tmp;
}

1

これを数学的に理解したい場合は、これがどのように機能するかなど

そのため、基本的にb / w -128から127までの数値は、10進数値と同じように書き込まれ、その(その数値-256)より上に書き込まれます。

例えば。132、答えは132-256 =-124すなわち

256 +あなたの答え256 +(-124)は132

もう一つの例

double a = 295.04;
int b = 300;
byte c = (byte) a;
byte d = (byte) b; System.out.println(c + " " + d);

出力は39 44になります

(295-256)(300-256)

注:小数点以下の数値は考慮されません。


0

概念的には、-128から+127の範囲になるまで、繰り返し256の減算が行われます。したがって、あなたの場合、あなたは132から始めて、1ステップで-124で終わります。

計算上、これは元の数値から最下位8ビットを抽出することに対応します。(これらの8つの最上位ビットが符号ビットになることに注意してください。)

他の言語ではこの動作は定義されていないことに注意してください(CやC ++など)。


明確にするために、得られる結果は、繰り返し減算が行われた場合と同じです。実際には、JVMは実際にはこの方法を実行しません。(それはひどく非効率的です!)
スティーブンC

確かに。2番目の段落で、JVMが実際にこれを行う方法について説明します。しかし、私は自分の言語を少しいじっています。
バトシェバ2017

1
はい。「本質的」から「概念的」への変化は大きな違いを生みます!
スティーブンC

-1
 N is input number
case 1: 0<=N<=127  answer=N;
case 2: 128<=N<=256 answer=N-256 
case 3: N>256   
        temp1=N/256;
        temp2=N-temp*256;
        if temp2<=127   then answer=temp2;
        else if temp2>=128  then answer=temp2-256;
case 4: negative  number input
        do same procedure.just change the sign of the solution           

正解は、除算や剰余ではなく、ビットマスキングによって到達します。
Lorneの侯爵2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.