พฤติกรรมแปลก ๆ เมื่อ Java แปลง int เป็น byte?


คำตอบ:


172

ใน Java intคือ 32 บิต byteคือ bits8

ชนิดดั้งเดิมมากที่สุดใน Java มีการเซ็นชื่อและbyte, short, intและlongมีการเข้ารหัสในการเติมเต็มสอง ( charประเภทนี้ไม่ได้ลงนามและแนวคิดของเครื่องหมายไม่สามารถใช้ได้กับboolean)

ในรูปแบบตัวเลขนี้บิตที่สำคัญที่สุดระบุเครื่องหมายของตัวเลข หากต้องการบิตมากขึ้นบิตที่สำคัญที่สุด ("MSB") จะถูกคัดลอกไปยัง MSB ใหม่

ดังนั้นถ้าคุณมีไบต์255: 11111111 และคุณต้องการแทนค่าเป็นint(32 บิต) คุณเพียงแค่คัดลอก 1 ไปทางซ้าย 24 ครั้ง

วิธีหนึ่งในการอ่านจำนวนบวกของสองลบคือเริ่มต้นด้วยบิตที่มีนัยสำคัญน้อยที่สุดเลื่อนไปทางซ้ายจนกว่าคุณจะพบ 1 แรกจากนั้นกลับด้านทุกบิตในภายหลัง ตัวเลขผลลัพธ์คือจำนวนบวกของตัวเลขนั้น

ตัวอย่างเช่น11111111ไป=00000001 -1นี่คือสิ่งที่ Java จะแสดงเป็นค่า

สิ่งที่คุณอาจต้องการทำคือรู้ค่าที่ไม่ได้ลงชื่อของไบต์

คุณสามารถทำได้ด้วย bitmask ที่ลบทุกอย่างยกเว้น 8 บิตที่มีนัยสำคัญน้อยที่สุด (0xff)

ดังนั้น:

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

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

จะพิมพ์ออกมา: "Signed: -1 Unsigned: 255"

เกิดอะไรขึ้นที่นี่?

เรากำลังใช้ bitwise AND เพื่อปกปิดบิตเครื่องหมายภายนอกทั้งหมด (ค่า 1 อยู่ทางซ้ายของ 8 บิตที่มีนัยสำคัญน้อยที่สุด) เมื่อ int ถูกแปลงเป็นไบต์ Java จะตัดส่วนที่เหลือ 24 บิตออกไป

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

เนื่องจากบิตที่ 32 เป็นบิตเครื่องหมายแทนบิตที่ 8 (และเราตั้งค่าบิตเครื่องหมายเป็น 0 ซึ่งเป็นค่าบวก) 8 บิตดั้งเดิมจากไบต์จะถูกอ่านโดย Java เป็นค่าบวก


1
ทำได้ดีคำอธิบายที่ดีที่สุดในเรื่องนี้เวย์น! ฉันแค่กำลังมองหาการจัดรูปแบบทางคณิตศาสตร์ว่าทำไมในการแสดงส่วนเสริมของทั้งสองจึงสามารถคัดลอกบิตเครื่องหมายทางด้านขวาเพื่อเพิ่มบิตได้ เป็นเรื่องง่ายที่จะเข้าใจว่ามันคิดตามกฎของการหาค่าลบของจำนวน นั่นคือ: พิจารณาบิตทั้งหมดจากขวาไปซ้ายและเขียนโดยไม่เปลี่ยนแปลงจนกว่า 1 แรกจะประกอบด้วย จากนั้นกลับบิตที่ตามมา ถ้าฉันคิดว่าบิตที่หายไปเป็น 0 มันง่ายที่จะเข้าใจว่าพวกเขาทั้งหมดไปที่ 1 แต่ฉันกำลังมองหาคำอธิบาย 'คณิตศาสตร์' เพิ่มเติม
AgostinoX

สิ่งที่signedByte & (0xff)เกิดขึ้นในที่0xffนี้คือมันเป็นตัวอักษร interger ดังนั้น signedByte จึงได้รับการเลื่อนขั้นเป็นจำนวนเต็มก่อนที่จะดำเนินการบิตไวซ์
Kevin Wheeler

นั่นไม่ใช่ 0xFF แต่เป็น 0x7E ในตัวอย่างของคุณ!
JohnyTex

89

132ในตัวเลข ( ฐาน 10 ) เป็น1000_0100บิต ( ฐาน 2 ) และ Java เก็บintใน 32 บิต:

0000_0000_0000_0000_0000_0000_1000_0100

อัลกอริทึมสำหรับ int-to-byte ถูกตัดด้านซ้าย อัลกอริทึม for System.out.printlnis two's -complement (Two's-complement is if leftmost bit is 1, ตีความเป็น negative -complement (invert bits) minus-one); ดังนั้นSystem.out.println(int-to-byte( ))คือ:

  • แปลความหมายเป็น (if-leftmost-bit-is-1 [negative (invert-bits (minus-one (] left-truncate ( 0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = แปลว่า (if-leftmost-bit-is-1 [negative (invert-bits (minus-one (] 1000_0100[)))])
  • = ตีความเป็น (ลบ (กลับบิต (ลบ - หนึ่ง ( 1000_0100))))
  • = ตีความเป็น (ลบ (กลับบิต ( 1000_0011)))
  • = ตีความเป็น (ลบ ( 0111_1100))
  • = ตีความตาม (ลบ (124))
  • = ตีความตาม (-124)
  • = -124 ธาดา !!!

7
อธิบายได้ดีมาก
ZAJ

1
ตอนนี้ 132 ในฐานสิบคือ -124 ในไบต์ การย้อนกลับทำงานอย่างไร?
Nilesh Deokar

@NileshDeokar การย้อนกลับเป็นของ POLA เนื่องจากพอดี (cf JLS 5.1.2 ); เอาต์พุตเกิดขึ้นพร้อมกับป้ายซ้าย ( 0สำหรับค่าบวกและ1ค่าลบ)
Pacerier

POLA คืออะไร? แปลงจากintไปbyteเป็นแปลงสูญเสีย (เช่นข้อมูลสูญหาย) ดังนั้นจึงไม่มีทางที่จะแปลงกลับเป็นintค่าเดิมได้
truthadjustr

23

ไบต์ใน Java ถูกเซ็นชื่อดังนั้นจึงมีช่วง -2 ^ 7 ถึง 2 ^ 7-1 - เช่น -128 ถึง 127 เนื่องจาก 132 สูงกว่า 127 คุณจะได้รับการรวมเป็น 132-256 = -124 นั่นคือโดยพื้นฐานแล้ว 256 (2 ^ 8) จะถูกเพิ่มหรือลบจนกว่าจะอยู่ในช่วง

สำหรับข้อมูลเพิ่มเติมคุณอาจต้องการที่จะอ่านข้อมูลเกี่ยวกับส่วนประกอบที่สอง


16

132 อยู่นอกช่วงของไบต์ซึ่งเป็น -128 ถึง 127 (Byte.MIN_VALUE ถึง Byte.MAX_VALUE) แทนค่าบิตบนสุดของค่า 8 บิตจะถือว่าเป็นเครื่องหมายซึ่งระบุว่าเป็นค่าลบในกรณีนี้ ดังนั้นจำนวนคือ 132 - 256 = -124


5

นี่เป็นวิธีเชิงกลที่ไม่มีทฤษฎีที่ทำให้ไขว้เขว:

  1. แปลงตัวเลขเป็นการแสดงไบนารี (ใช้เครื่องคิดเลขได้ไหม)
  2. คัดลอกเฉพาะ 8 บิตขวาสุด (LSB) และทิ้งส่วนที่เหลือ
  3. จากผลลัพธ์ของขั้นตอน # 2 ถ้าบิตซ้ายสุดเป็น 0 ให้ใช้เครื่องคิดเลขเพื่อแปลงตัวเลขเป็นทศนิยม นี่คือคำตอบของคุณ
  4. อื่น ๆ (ถ้าบิตซ้ายสุดคือ 1) คำตอบของคุณเป็นลบ ปล่อยให้ศูนย์ขวาสุดทั้งหมดและบิตที่ไม่ใช่ศูนย์แรกไม่เปลี่ยนแปลง และกลับรายการที่เหลือนั่นคือแทนที่ 1 ด้วย 0 และ 0 ด้วย 1 จากนั้นใช้เครื่องคิดเลขเพื่อแปลงเป็นทศนิยมและต่อท้ายเครื่องหมายลบเพื่อระบุว่าค่าเป็นลบ

วิธีการที่เป็นประโยชน์มากขึ้นนี้เป็นไปตามคำตอบทางทฤษฎีข้างต้น ดังนั้นผู้ที่ยังอ่านหนังสือ Java ที่บอกว่าจะใช้โมดูโล่นี่เป็นสิ่งที่ผิดอย่างแน่นอนเนื่องจาก 4 ขั้นตอนที่ฉันระบุไว้ข้างต้นไม่ใช่การทำงานของโมดูโลอย่างแน่นอน


สิ่งที่หนังสือ Java พูดกับการใช้งาน 'โมดูโล'? ฉันไม่เคยเห็นหนังสือ CS เล่มไหนที่ระบุว่าในปี 46 นับประสาอะไรกับหนังสือ Java เล่มใดเลย 'โมดูโล' คืออะไร? ไม่มีการดำเนินการโมดูโลใน Java ตัวดำเนินการที่เหลือเท่านั้น
Marquis of Lorne

grep ยากขึ้น http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdfหน้า 59
truthadjustr

4

สมการเสริมของสอง:

ป้อนคำอธิบายภาพที่นี่


ใน Java byte(N = 8) และint(N = 32) จะแสดงโดย 2s-complement ที่แสดงด้านบน

จากสมการที่7เป็นลบสำหรับแต่ในเชิงบวกสำหรับbyteint

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 เป็นไบต์ว่าดำเนินการโดยการหารโมดูลัส สิ่งนี้ไม่ถูกต้องอย่างเคร่งครัดดังที่แสดงด้านล่างสิ่งที่เกิดขึ้นจริงคือ 24 บิตที่สำคัญที่สุดจากค่าไบนารีของจำนวน int จะถูกละทิ้งทำให้เกิดความสับสนหากตั้งค่าบิตซ้ายสุดที่เหลือซึ่งกำหนดจำนวนเป็นค่าลบ

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 ปี
Marquis of Lorne

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 จะเขียนเช่นเดียวกับค่าทศนิยมเหนือกว่า (ตัวเลขของคุณ - 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

ตามแนวคิดแล้วการลบซ้ำ ๆ ของ 256 จะทำกับตัวเลขของคุณจนกว่าจะอยู่ในช่วง -128 ถึง +127 ในกรณีของคุณคุณเริ่มต้นด้วย 132 แล้วลงท้ายด้วย -124 ในขั้นตอนเดียว

ในทางคำนวณสิ่งนี้สอดคล้องกับการแยก 8 บิตที่มีนัยสำคัญน้อยที่สุดจากจำนวนเดิมของคุณ (และโปรดทราบว่าบิตที่สำคัญที่สุดของ 8 เหล่านี้กลายเป็นบิตเครื่องหมาย)

โปรดทราบว่าในภาษาอื่นไม่ได้กำหนดลักษณะการทำงานนี้ (เช่น C และ C ++)


ต้องมีความชัดเจนผลที่คุณได้รับจะเหมือนกันเช่นถ้า subtractions ซ้ำได้ทำ ในทางปฏิบัติ JVM ไม่ได้ทำในลักษณะนี้ (มันจะไร้ประสิทธิภาพอย่างมาก!)
Stephen C

จริง ฉันหวังว่าย่อหน้าที่สองของฉันจะครอบคลุมถึงวิธีที่ JVM ทำสิ่งนี้จริงๆ แต่ฉันได้เล่นกับภาษาของฉันเล็กน้อย
Bathsheba

1
ใช่. การเปลี่ยนแปลง "ตามหลัก" เป็น "แนวคิด" สร้างความแตกต่างอย่างมาก!
Stephen 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           

คำตอบที่ถูกต้องมาจากการมาสก์บิตไม่ใช่โดยการหารและส่วนที่เหลือ
Marquis of Lorne
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.