รหัสนี้:
System.out.println(Math.abs(Integer.MIN_VALUE));
ผลตอบแทน -2147483648
มันไม่ควรคืนค่าสัมบูรณ์เป็น2147483648?
รหัสนี้:
System.out.println(Math.abs(Integer.MIN_VALUE));
ผลตอบแทน -2147483648
มันไม่ควรคืนค่าสัมบูรณ์เป็น2147483648?
คำตอบ:
Integer.MIN_VALUEเป็น-2147483648แต่มูลค่าสูงสุด 32 +2147483647บิตจำนวนเต็มสามารถมีคือ ความพยายามที่จะเป็นตัวแทน+2147483648ใน int 32 บิตจะได้อย่างมีประสิทธิภาพ "เกลือกกลิ้ง" -2147483648เพื่อ นี้เป็นเพราะเมื่อใช้ลงนามจำนวนเต็มสองของเสริมของเลขฐานสอง+2147483648และ-2147483648เหมือนกัน อย่างไรก็ตามนี่ไม่ใช่ปัญหา แต่+2147483648ถือว่าอยู่นอกขอบเขต
สำหรับการอ่านน้อยมากเกี่ยวกับเรื่องนี้คุณอาจต้องการที่จะตรวจสอบบทความวิกิพีเดียเติมเต็มสองของ
พฤติกรรมที่คุณชี้ให้เห็นนั้นขัดแย้งกับธรรมชาติ อย่างไรก็ตามพฤติกรรมนี้เป็นลักษณะที่ระบุโดยjavadoc สำหรับMath.abs(int) :
ถ้าอาร์กิวเมนต์ไม่เป็นลบอาร์กิวเมนต์จะถูกส่งกลับ ถ้าอาร์กิวเมนต์เป็นลบการปฏิเสธของอาร์กิวเมนต์จะถูกส่งกลับ
นั่นคือMath.abs(int)ควรทำงานเหมือนโค้ด Java ต่อไปนี้:
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
-xนั่นคือในกรณีที่เชิงลบ
ตามที่ส่วน JLS 15.15.4ที่-xเท่ากับ(~x)+1ที่~เป็นผู้ประกอบการระดับบิตสมบูรณ์
หากต้องการตรวจสอบว่าเสียงถูกต้องหรือไม่ลองใช้ -1 เป็นตัวอย่าง
ค่าจำนวนเต็ม-1สามารถระบุได้เป็น0xFFFFFFFFเลขฐานสิบหกใน Java (ตรวจสอบสิ่งนี้ด้วย a printlnหรือวิธีการอื่น ๆ ) การดำเนินการดังกล่าว-(-1)จะช่วยให้:
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
ดังนั้นมันได้ผล
Integer.MIN_VALUEให้เราพยายามที่ตอนนี้มี เมื่อทราบว่าจำนวนเต็มต่ำสุดสามารถแสดงได้0x80000000นั่นคือบิตแรกที่ตั้งค่าเป็น 1 และ 31 บิตที่เหลือตั้งค่าเป็น 0 เรามี:
-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
และนี่คือเหตุผลที่Math.abs(Integer.MIN_VALUE)กลับInteger.MIN_VALUEมา นอกจากนี้ทราบว่าเป็น0x7FFFFFFFInteger.MAX_VALUE
ที่กล่าวว่าเราจะหลีกเลี่ยงปัญหาอันเนื่องมาจากมูลค่าผลตอบแทนที่ตอบโต้ได้ง่ายในอนาคตได้อย่างไร
เราสามารถ, เป็นแหลมออกโดย @Bombeหล่อของเราintที่จะlongก่อน อย่างไรก็ตามเราต้องอย่างใดอย่างหนึ่ง
ints
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)ซึ่งไม่ได้เพราะlongs อย่างใดหวังว่าเราจะไม่เรียกร้องMath.abs(long)ที่มีค่าเท่ากับตั้งแต่เรายังมีLong.MIN_VALUEMath.abs(Long.MIN_VALUE) == Long.MIN_VALUEเราสามารถใช้BigIntegers ได้ทุกที่เพราะBigInteger.abs()จะคืนค่าเป็นบวกเสมอ นี่เป็นทางเลือกที่ดีแม้ว่าจะช้ากว่าการจัดการประเภทจำนวนเต็มดิบเล็กน้อย
เราสามารถเขียนกระดาษห่อของเราเองได้Math.abs(int)ดังนี้:
/**
* Fail-fast wrapper for {@link Math#abs(int)}
* @param x
* @return the absolute value of x
* @throws ArithmeticException when a negative value would have been returned by {@link Math#abs(int)}
*/
public static int abs(int x) throws ArithmeticException {
if (x == Integer.MIN_VALUE) {
// fail instead of returning Integer.MAX_VALUE
// to prevent the occurrence of incorrect results in later computations
throw new ArithmeticException("Math.abs(Integer.MIN_VALUE)");
}
return Math.abs(x);
}
int positive = value & Integer.MAX_VALUE(โดยพื้นฐานแล้วจะล้นจากInteger.MAX_VALUEถึง0แทนที่จะเป็นInteger.MIN_VALUE)ในท้ายที่สุดปัญหานี้ดูเหมือนจะเป็นที่ทราบกันดีอยู่แล้ว ดูตัวอย่างรายการนี้เกี่ยวกับกฎ findbugs ที่เกี่ยวข้อง
นี่คือสิ่งที่ Java doc พูดสำหรับ Math.abs () ในjavadoc :
โปรดสังเกตว่าถ้าอาร์กิวเมนต์เท่ากับค่า Integer.MIN_VALUE ซึ่งเป็นค่า int ที่เป็นค่าลบมากที่สุดผลลัพธ์จะเป็นค่าเดียวกันซึ่งเป็นค่าลบ
หากต้องการดูผลลัพธ์ที่คุณคาดหวังให้ส่งInteger.MIN_VALUEไปที่long:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.absถูกต่อต้านโดยการคืนค่าจำนวนลบ:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException? นอกจากนี้พฤติกรรมนี้ยังได้รับการบันทึกไว้อย่างชัดเจนในเอกสาร API
Math.abs(long)ฉันแค่อยากจะชี้ให้เห็นว่าพฤติกรรมนี้ซึ่งเป็นแหล่งที่มาของข้อบกพร่องที่ไม่ได้รับการแก้ไขโดยการใช้งานของ ขออภัยในความผิดพลาดของฉันที่นี่: ฉันคิดว่าคุณเสนอให้ใช้Math.abs(long)เพื่อแก้ไขเมื่อคุณแสดงให้เห็นว่าเป็นวิธีง่ายๆในการ "เห็นผลลัพธ์ที่ผู้ถามคาดหวัง" ขออภัย.
2147483648 ไม่สามารถเก็บเป็นจำนวนเต็มใน java ได้การแสดงไบนารีจะเหมือนกับ -2147483648
แต่(int) 2147483648L == -2147483648 มีจำนวนลบหนึ่งตัวที่ไม่มีค่าเทียบเท่าบวกดังนั้นจึงไม่มีค่าบวกสำหรับมัน คุณจะเห็นพฤติกรรมเดียวกันกับ Long.MAX_VALUE
มีการแก้ไขนี้ใน Java 15 จะเป็นวิธีการ int และยาว พวกเขาจะนำเสนอในชั้นเรียน
java.lang.Math and java.lang.StrictMath
วิธีการ
public static int absExact(int a)
public static long absExact(long a)
ถ้าคุณผ่าน
Integer.MIN_VALUE
หรือ
Long.MIN_VALUE
ข้อยกเว้นถูกโยนทิ้ง
https://bugs.openjdk.java.net/browse/JDK-8241805
ฉันต้องการดูว่า Long.MIN_VALUE หรือ Integer.MIN_VALUE ถูกส่งผ่านค่าบวกจะได้รับผลตอบแทนไม่ใช่ข้อยกเว้น แต่