Mã này:
System.out.println(Math.abs(Integer.MIN_VALUE));
Lợi nhuận -2147483648
Nó không nên trả về giá trị tuyệt đối là 2147483648
?
Mã này:
System.out.println(Math.abs(Integer.MIN_VALUE));
Lợi nhuận -2147483648
Nó không nên trả về giá trị tuyệt đối là 2147483648
?
Câu trả lời:
Integer.MIN_VALUE
là -2147483648
, nhưng giá trị cao nhất mà một số nguyên 32 bit có thể chứa là +2147483647
. Cố gắng biểu diễn +2147483648
trong 32 bit int sẽ có hiệu quả "cuộn qua" đến -2147483648
. Điều này là do, khi sử dụng số nguyên có dấu, hai biểu diễn nhị phân bổ sung của +2147483648
và -2147483648
là giống hệt nhau. Đây không phải là một vấn đề, tuy nhiên, +2147483648
được coi là nằm ngoài phạm vi.
Để đọc thêm một chút về vấn đề này, bạn có thể muốn xem bài viết trên Wikipedia về phần bổ sung của Two .
Hành vi bạn chỉ ra thực sự là phản trực giác. Tuy nhiên, hành vi này là hành vi được javadocMath.abs(int)
chỉ định cho :
Nếu đối số không phủ định, đối số được trả về. Nếu đối số là phủ định, thì phủ định của đối số được trả về.
Đó là, Math.abs(int)
sẽ hoạt động giống như mã Java sau:
public static int abs(int x){
if (x >= 0) {
return x;
}
return -x;
}
Đó là, trong trường hợp tiêu cực -x
,.
Theo JLS phần 15.15.4 , -x
bằng (~x)+1
, ở đâu ~
là toán tử bổ sung bit.
Để kiểm tra xem điều này có đúng không, hãy lấy -1 làm ví dụ.
Giá trị số nguyên -1
có thể được ghi chú như 0xFFFFFFFF
trong hệ thập lục phân trong Java (hãy kiểm tra điều này bằng a println
hoặc bất kỳ phương thức nào khác). Lấy -(-1)
do đó cung cấp cho:
-(-1) = (~(0xFFFFFFFF)) + 1 = 0x00000000 + 1 = 0x00000001 = 1
Vì vậy, nó hoạt động.
Hãy để chúng tôi thử ngay bây giờ với Integer.MIN_VALUE
. Biết rằng số nguyên thấp nhất có thể được biểu diễn bằng 0x80000000
, nghĩa là, bit đầu tiên được đặt thành 1 và 31 bit còn lại được đặt thành 0, chúng ta có:
-(Integer.MIN_VALUE) = (~(0x80000000)) + 1 = 0x7FFFFFFF + 1
= 0x80000000 = Integer.MIN_VALUE
Và đây là lý do tại sao Math.abs(Integer.MIN_VALUE)
trả về Integer.MIN_VALUE
. Cũng lưu ý rằng 0x7FFFFFFF
là Integer.MAX_VALUE
.
Điều đó nói rằng, làm thế nào chúng ta có thể tránh các vấn đề do giá trị trả về phản trực giác này trong tương lai?
Chúng tôi có thể, như ra nhọn bởi @Bombe , đúc của chúng tôi int
s đến long
trước. Tuy nhiên, chúng ta phải
int
s, không hoạt động bởi vì
Integer.MIN_VALUE == (int) Math.abs((long)Integer.MIN_VALUE)
.long
s bằng cách nào đó hy vọng rằng chúng ta sẽ không bao giờ gọi Math.abs(long)
với giá trị bằng Long.MIN_VALUE
, vì chúng ta cũng có Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
.Chúng ta có thể sử dụng BigInteger
s ở mọi nơi, vì BigInteger.abs()
nó thực sự luôn trả về một giá trị dương. Đây là một giải pháp thay thế tốt, mặc dù chậm hơn một chút so với thao tác với các kiểu số nguyên thô.
Chúng ta có thể viết trình bao bọc của riêng mình Math.abs(int)
, như thế này:
/**
* 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
(về cơ bản tràn từ Integer.MAX_VALUE
sang 0
thay vì Integer.MIN_VALUE
)Lưu ý cuối cùng, vấn đề này dường như đã được biết đến trong một thời gian. Xem ví dụ mục nhập này về quy tắc findbugs tương ứng .
Để xem kết quả mà bạn mong đợi, hãy truyền Integer.MIN_VALUE
tới long
:
System.out.println(Math.abs((long) Integer.MIN_VALUE));
Math.abs
đang được phản trực giác bằng cách trả lại một số âm:Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE
ArithmeticException
cái? Ngoài ra, hành vi được ghi lại rõ ràng trong tài liệu API.
Math.abs(long)
. Tôi xin lỗi vì sai lầm của tôi ở đây: Tôi đã nghĩ rằng bạn đề xuất sử dụng Math.abs(long)
như một bản sửa lỗi, khi bạn cho thấy nó như một cách đơn giản để "xem kết quả mà người hỏi đang mong đợi". Lấy làm tiếc.
Nhưng (int) 2147483648L == -2147483648
có một số âm không có giá trị dương tương đương nên không có giá trị dương cho nó. Bạn sẽ thấy hành vi tương tự với Long.MAX_VALUE.
Có một bản sửa lỗi cho điều này trong Java 15 sẽ là một phương thức để int và long. Họ sẽ có mặt trên các lớp học
java.lang.Math and java.lang.StrictMath
Các phương pháp.
public static int absExact(int a)
public static long absExact(long a)
Nếu bạn vượt qua
Integer.MIN_VALUE
HOẶC LÀ
Long.MIN_VALUE
Một Exception được ném ra.
https://bugs.openjdk.java.net/browse/JDK-8241805
Tôi muốn xem nếu Long.MIN_VALUE hoặc Integer.MIN_VALUE được chuyển một giá trị dương sẽ được trả về và không phải là một ngoại lệ.
Math.abs không hoạt động mọi lúc với những con số lớn. Tôi sử dụng logic mã nhỏ này mà tôi đã học được khi tôi 7 tuổi!
if(Num < 0){
Num = -(Num);
}
s
ở đây?
Num
bằng Integer.MIN_VALUE
trước đoạn mã?