Làm thế nào để Java xử lý số nguyên tràn và tràn và bạn sẽ kiểm tra nó như thế nào?


226

Làm thế nào để Java xử lý số nguyên tràn và tràn?

Từ đó, bạn sẽ kiểm tra / kiểm tra xem điều này có xảy ra không?


28
Thật tệ khi Java không cung cấp quyền truy cập gián tiếp vào cờ tràn của CPU , như được thực hiện trong C # .
Drew Noakes

@DrewNoakes Và thật tệ khi C # không mặc định checkedtheo như tôi biết. Tôi không thấy nó được sử dụng nhiều, và gõ checked { code; }cũng giống như gọi một phương thức.
Maarten Bodewes

2
@MaartenBodewes, bạn có thể đặt nó làm mặc định trong quá trình biên dịch lắp ráp. csc /checked ...hoặc đặt thuộc tính trong ngăn thuộc tính của dự án trong Visual Studio.
Drew Noakes

@DrewKhông OK, thú vị. Một điều kỳ lạ là nó là một thiết lập bên ngoài mã. Nói chung, tôi muốn có cùng một hành vi của một chương trình bất kể các cài đặt đó (có thể ngoại trừ các xác nhận).
Maarten Bodewes

@MaartenBodewes, tôi nghĩ lý do là có một chi phí hoàn hảo không tầm thường để kiểm tra. Vì vậy, có lẽ bạn sẽ kích hoạt nó trong các bản dựng gỡ lỗi, sau đó vô hiệu hóa nó trong các bản dựng phát hành, giống như nhiều loại xác nhận khác.
vẽ Noakes

Câu trả lời:


217

Nếu nó tràn ra, nó sẽ quay trở lại giá trị tối thiểu và tiếp tục từ đó. Nếu nó chảy xuống, nó sẽ quay trở lại giá trị tối đa và tiếp tục từ đó.

Bạn có thể kiểm tra trước như sau:

public static boolean willAdditionOverflow(int left, int right) {
    if (right < 0 && right != Integer.MIN_VALUE) {
        return willSubtractionOverflow(left, -right);
    } else {
        return (~(left ^ right) & (left ^ (left + right))) < 0;
    }
}

public static boolean willSubtractionOverflow(int left, int right) {
    if (right < 0) {
        return willAdditionOverflow(left, -right);
    } else {
        return ((left ^ right) & (left ^ (left - right))) < 0;
    }
}

(bạn có thể thay thế intbằng cách longthực hiện các kiểm tra tương tự cho long)

Nếu bạn nghĩ rằng điều này có thể xảy ra nhiều hơn thường xuyên, thì hãy xem xét sử dụng một kiểu dữ liệu hoặc đối tượng có thể lưu trữ các giá trị lớn hơn, ví dụ longhoặc có thể java.math.BigInteger. Cái cuối cùng không tràn, thực tế, bộ nhớ JVM có sẵn là giới hạn.


Nếu bạn đã có trên Java8, thì bạn có thể sử dụng các phương thức mới Math#addExact()Math#subtractExact()phương thức sẽ tạo ra một ArithmeticExceptionlỗi tràn.

public static boolean willAdditionOverflow(int left, int right) {
    try {
        Math.addExact(left, right);
        return false;
    } catch (ArithmeticException e) {
        return true;
    }
}

public static boolean willSubtractionOverflow(int left, int right) {
    try {
        Math.subtractExact(left, right);
        return false;
    } catch (ArithmeticException e) {
        return true;
    }
}

Mã nguồn có thể được tìm thấy ở đâyở đây tương ứng.

Tất nhiên, bạn cũng có thể sử dụng chúng ngay lập tức thay vì ẩn chúng trong một booleanphương thức tiện ích.


13
@dhblah, giả sử giá trị tối đa và tối thiểu mà Java cho phép cho một int +100, -100tương ứng. Nếu bạn đã thêm một vào một số nguyên Java, quá trình sẽ như thế này khi nó tràn ra. 98, 99, 100, -100, -99, -98, .... Điều đó có làm cho nó ý nghĩa hơn không?
Austin A

6
Tôi khuyên bạn nên sử dụng các phương thức tiện ích thay vì sử dụng mã ngay lập tức. Các phương thức tiện ích là nội tại và sẽ được thay thế bằng mã cụ thể của máy. Một thử nghiệm nhanh cho thấy Math.addExact nhanh hơn 30% so với phương thức được sao chép (Java 1.8.0_40).
TilmannZ

1
@ErikE Math#addExactlà cú pháp thường được sử dụng khi viết javadocs - trong khi thông thường sẽ được chuyển đổi thành Math.addExact, đôi khi hình thức khác chỉ xuất hiện xung quanh
Pokechu22

1
If it underflows, it goes back to the maximum value and continues from there.- bạn dường như đã nhầm lẫn với tràn âm. dòng chảy trong số nguyên xảy ra mọi lúc (khi kết quả là một phân số).
gian giữa

1
vi.wikipedia.org/wiki/Arithatures_underflow nói rằng Underflow là một điều kiện trong chương trình máy tính trong đó kết quả của phép tính là một số giá trị tuyệt đối nhỏ hơn so với máy tính thực sự có thể biểu thị trong bộ nhớ trên CPU của nó. Vì vậy, underflow không áp dụng cho Số nguyên Java. @BalusC
Jingguo Yao

66

Chà, theo như các kiểu số nguyên nguyên, Java hoàn toàn không xử lý Over / Underflow (đối với float và nhân đôi hành vi là khác nhau, nó sẽ tuôn ra +/- vô hạn như các nhiệm vụ của IEEE-754).

Khi thêm hai int, bạn sẽ không nhận được dấu hiệu nào khi xảy ra tràn. Một phương pháp đơn giản để kiểm tra tràn là sử dụng loại lớn hơn tiếp theo để thực sự thực hiện thao tác và kiểm tra xem kết quả có còn trong phạm vi cho loại nguồn không:

public int addWithOverflowCheck(int a, int b) {
    // the cast of a is required, to make the + work with long precision,
    // if we just added (a + b) the addition would use int precision and
    // the result would be cast to long afterwards!
    long result = ((long) a) + b;
    if (result > Integer.MAX_VALUE) {
         throw new RuntimeException("Overflow occured");
    } else if (result < Integer.MIN_VALUE) {
         throw new RuntimeException("Underflow occured");
    }
    // at this point we can safely cast back to int, we checked before
    // that the value will be withing int's limits
    return (int) result;
}

Những gì bạn sẽ làm thay cho các mệnh đề ném, tùy thuộc vào yêu cầu ứng dụng của bạn (ném, tuôn ra tối thiểu / tối đa hoặc chỉ cần đăng nhập bất cứ điều gì). Nếu bạn muốn phát hiện tràn trong các hoạt động dài, bạn không gặp may với người nguyên thủy, thay vào đó hãy sử dụng BigInteger.


Chỉnh sửa (2014-05-21): Vì câu hỏi này dường như được nhắc đến khá thường xuyên và tôi đã phải tự giải quyết vấn đề tương tự, nên việc đánh giá điều kiện tràn bằng phương pháp tương tự mà CPU sẽ tính toán cờ V của nó là khá dễ dàng.

Về cơ bản, nó là một biểu thức boolean liên quan đến dấu của cả hai toán hạng cũng như kết quả:

/**
 * Add two int's with overflow detection (r = s + d)
 */
public static int add(final int s, final int d) throws ArithmeticException {
    int r = s + d;
    if (((s & d & ~r) | (~s & ~d & r)) < 0)
        throw new ArithmeticException("int overflow add(" + s + ", " + d + ")");    
    return r;
}

Trong java đơn giản hơn để áp dụng biểu thức (trong if) cho toàn bộ 32 bit và kiểm tra kết quả bằng cách sử dụng <0 (điều này sẽ kiểm tra hiệu quả bit dấu). Nguyên tắc hoạt động chính xác như nhau cho tất cả các kiểu nguyên nguyên , thay đổi tất cả các khai báo trong phương thức trên thành dài làm cho nó hoạt động lâu dài.

Đối với các loại nhỏ hơn, do chuyển đổi ngầm định thành int (xem JLS để biết chi tiết các thao tác bitwise), thay vì kiểm tra <0, kiểm tra cần che dấu bit rõ ràng (0x8000 cho toán hạng ngắn, 0x80 cho toán hạng byte, điều chỉnh phôi và khai báo tham số thích hợp):

/**
 * Subtract two short's with overflow detection (r = d - s)
 */
public static short sub(final short d, final short s) throws ArithmeticException {
    int r = d - s;
    if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0)
        throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")");
    return (short) r;
}

(Lưu ý rằng ví dụ trên sử dụng biểu thức cần để trừ phát hiện tràn)


Vậy làm thế nào / tại sao các biểu thức boolean này hoạt động? Đầu tiên, một số suy nghĩ logic cho thấy rằng một tràn chỉ có thể xảy ra nếu các dấu hiệu của cả hai đối số là như nhau. Bởi vì, nếu một đối số là âm và một đối số dương, kết quả (của add) phải gần bằng 0 hơn hoặc trong trường hợp cực đoan, một đối số bằng 0, giống như đối số khác. Vì các đối số tự chúng không thể tạo điều kiện tràn, nên tổng của chúng cũng không thể tạo ra tràn.

Vậy điều gì xảy ra nếu cả hai đối số có cùng dấu? Hãy xem xét cả hai trường hợp đều dương: thêm hai đối số tạo ra tổng lớn hơn các loại MAX_VALUE, sẽ luôn mang lại giá trị âm, do đó xảy ra tràn nếu arg1 + arg2> MAX_VALUE. Bây giờ giá trị tối đa có thể dẫn đến sẽ là MAX_VALUE + MAX_VALUE (trường hợp cực đoan cả hai đối số là MAX_VALUE). Đối với một byte (ví dụ) có nghĩa là 127 + 127 = 254. Nhìn vào các biểu diễn bit của tất cả các giá trị có thể dẫn đến việc thêm hai giá trị dương, người ta thấy rằng các giá trị tràn (128 đến 254) đều có bit 7, trong khi tất cả những gì không tràn (0 đến 127) đều bị xóa bit 7 (trên cùng, dấu hiệu). Đó chính xác là phần đầu tiên (bên phải) của biểu thức kiểm tra:

if (((s & d & ~r) | (~s & ~d & r)) < 0)

(~ s & ~ d & r) trở thành đúng, chỉ khi , cả hai toán hạng (s, d) đều dương và kết quả (r) là âm (biểu thức hoạt động trên tất cả 32 bit, nhưng bit duy nhất chúng ta quan tâm là bit (dấu) trên cùng, được kiểm tra bằng <0).

Bây giờ nếu cả hai đối số đều âm, tổng của chúng không bao giờ có thể gần bằng 0 hơn bất kỳ đối số nào, tổng phải gần với âm vô hạn. Giá trị cực đoan nhất mà chúng tôi có thể tạo ra là MIN_VALUE + MIN_VALUE, một lần nữa (ví dụ cho byte) cho thấy với bất kỳ giá trị nào trong phạm vi (-1 đến -128), bit dấu được đặt, trong khi mọi giá trị có thể tràn (-129 đến -256 ) đã xóa bit dấu. Vì vậy, dấu hiệu của kết quả một lần nữa cho thấy tình trạng tràn. Đó là những gì nửa bên trái (s & d & ~ r) kiểm tra cho trường hợp cả hai đối số (s, d) đều âm và kết quả là dương. Logic phần lớn tương đương với trường hợp tích cực; tất cả các mẫu bit có thể dẫn đến việc thêm hai giá trị âm sẽ bị xóa bit dấu nếu và chỉ khi xảy ra tràn dòng.


1
Bạn có thể kiểm tra nó với các toán tử bitwise, cũng như betterlogic.com/roger/2011/05/ Thẻ
rogerdpack

1
Điều này sẽ hoạt động nhưng tôi cho rằng nó sẽ có một hiệu suất khó chịu.
cờ vua

33

Theo mặc định, toán học int và long của Java âm thầm bao quanh tràn và tràn. (Các thao tác số nguyên trên các loại số nguyên khác được thực hiện bằng cách trước tiên quảng bá các toán hạng thành int hoặc long, theo JLS 4.2.2 .)

Tính đến Java 8, java.lang.Mathcung cấp addExact, subtractExact, multiplyExact, incrementExact, decrementExactnegateExactcác phương pháp tĩnh cho cả int và lập luận dài mà thực hiện các hoạt động được đặt tên, ném ArithmeticException trên tràn. (Không có phương thức splitExact - bạn sẽ phải tự kiểm tra một trường hợp đặc biệt ( MIN_VALUE / -1).)

Kể từ Java 8, java.lang.Math cũng cung cấp toIntExactđể truyền dài cho một int, ném ArithaturesException nếu giá trị của long không khớp với int. Điều này có thể hữu ích cho việc tính toán tổng số toIntExactint bằng toán học dài không được kiểm tra, sau đó sử dụng để chuyển sang int ở cuối (nhưng hãy cẩn thận đừng để tổng của bạn bị tràn).

Nếu bạn vẫn đang sử dụng phiên bản Java cũ hơn, Google Guava cung cấp các phương thức tĩnh IntMath và LongMath để kiểm tra thêm, trừ, nhân và lũy thừa (ném tràn). Các lớp này cũng cung cấp các phương thức để tính các yếu tố và hệ số nhị thức trả MAX_VALUEvề khi tràn (không thuận tiện để kiểm tra). Lớp tiện ích nguyên thủy ổi của, SignedBytes, UnsignedBytes, ShortsInts, cung cấp checkedCastphương pháp để thu hẹp các loại lớn hơn (ném IllegalArgumentException trên dưới / tràn, không ArithmeticException), cũng như saturatingCastphương pháp mà trở lại MIN_VALUEhoặc MAX_VALUEtrên tràn.


32

Java không làm bất cứ điều gì với tràn số nguyên cho các kiểu nguyên thủy int hoặc dài và bỏ qua tràn với các số nguyên dương và âm.

Câu trả lời này trước tiên mô tả về tràn số nguyên, đưa ra một ví dụ về cách nó có thể xảy ra, ngay cả với các giá trị trung gian trong đánh giá biểu thức và sau đó đưa ra các liên kết đến các tài nguyên cung cấp các kỹ thuật chi tiết để ngăn chặn và phát hiện tràn số nguyên.

Số học và biểu thức số nguyên cộng lại trong tràn bất ngờ hoặc không bị phát hiện là một lỗi lập trình phổ biến. Tràn số nguyên bất ngờ hoặc không bị phát hiện cũng là một vấn đề bảo mật có thể khai thác nổi tiếng, đặc biệt là nó ảnh hưởng đến các đối tượng mảng, ngăn xếp và liệt kê.

Tràn có thể xảy ra theo hướng tích cực hoặc tiêu cực trong đó giá trị dương hoặc âm sẽ vượt quá giá trị tối đa hoặc tối thiểu đối với loại nguyên thủy được đề cập. Sự tràn có thể xảy ra trong một giá trị trung gian trong quá trình đánh giá biểu thức hoặc hoạt động và ảnh hưởng đến kết quả của biểu thức hoặc hoạt động trong đó giá trị cuối cùng sẽ được dự kiến ​​nằm trong phạm vi.

Đôi khi tràn âm được gọi nhầm là tràn. Underflow là những gì xảy ra khi một giá trị sẽ gần bằng 0 hơn mức đại diện cho phép. Dòng chảy xảy ra trong số học số nguyên và dự kiến. Dòng dưới số nguyên xảy ra khi đánh giá số nguyên nằm trong khoảng -1 đến 0 hoặc 0 và 1. Kết quả phân số sẽ rút ngắn thành 0. Điều này là bình thường và được mong đợi với số học số nguyên và không được coi là lỗi. Tuy nhiên, nó có thể dẫn đến việc ném mã ngoại lệ. Một ví dụ là một ngoại lệ "ArithaturesException: / by zero" nếu kết quả của dòng dưới số nguyên được sử dụng như một ước số trong một biểu thức.

Hãy xem xét các mã sau đây:

int bigValue = Integer.MAX_VALUE;
int x = bigValue * 2 / 5;
int y = bigValue / x;

dẫn đến x được gán 0 và đánh giá tiếp theo của bigValue / x đưa ra một ngoại lệ, "ArithaturesException: / by zero" (nghĩa là chia cho 0), thay vì y được gán giá trị 2.

Kết quả dự kiến ​​cho x sẽ là 858.993.458 nhỏ hơn giá trị int tối đa là 2.147.483.647. Tuy nhiên, kết quả trung gian từ việc đánh giá Integer.MAX_Value * 2, sẽ là 4.294.967.294, vượt quá giá trị int tối đa và là -2 theo biểu diễn số nguyên bổ sung 2s. Đánh giá tiếp theo của -2 / 5 ước tính thành 0 được gán cho x.

Sắp xếp lại biểu thức để tính x thành biểu thức mà khi được đánh giá sẽ chia trước khi nhân, mã sau:

int bigValue = Integer.MAX_VALUE;
int x = bigValue / 5 * 2;
int y = bigValue / x;

kết quả là x được chỉ định 858.993.458 và y được chỉ định 2, dự kiến.

Kết quả trung gian từ bigValue / 5 là 429.496.729, không vượt quá giá trị tối đa cho một int. Đánh giá sau đó của 429.496.729 * 2 không vượt quá giá trị tối đa cho một int và kết quả mong đợi được gán cho x. Việc đánh giá cho y sau đó không chia cho số không. Các đánh giá cho x và y hoạt động như mong đợi.

Các giá trị số nguyên Java được lưu trữ dưới dạng và hoạt động theo các biểu diễn số nguyên đã ký 2s. Khi một giá trị kết quả sẽ lớn hơn hoặc nhỏ hơn giá trị số nguyên tối đa hoặc tối thiểu, thay vào đó sẽ có kết quả giá trị số nguyên bổ sung 2. Trong các tình huống không được thiết kế rõ ràng để sử dụng hành vi bổ sung 2s, đó là hầu hết các tình huống số học thông thường, giá trị bổ sung 2s kết quả sẽ gây ra lỗi lập trình hoặc lỗi tính toán như trong ví dụ trên. Một bài viết Wikipedia xuất sắc mô tả số nguyên nhị phân khen ngợi 2s ở đây: Bổ sung của hai - Wikipedia

Có các kỹ thuật để tránh tràn số nguyên không chủ ý. Techinques có thể được phân loại là sử dụng thử nghiệm tiền điều kiện, phát sóng và BigInteger.

Kiểm tra tiền điều kiện bao gồm kiểm tra các giá trị đi vào hoạt động hoặc biểu thức số học để đảm bảo rằng tràn sẽ không xảy ra với các giá trị đó. Lập trình và thiết kế sẽ cần tạo thử nghiệm để đảm bảo các giá trị đầu vào sẽ không gây ra tràn và sau đó xác định phải làm gì nếu giá trị đầu vào xảy ra sẽ gây ra tràn.

Upcasting bao gồm sử dụng một kiểu nguyên thủy lớn hơn để thực hiện phép toán hoặc biểu thức số học và sau đó xác định xem giá trị kết quả có vượt quá giá trị tối đa hoặc tối thiểu cho một số nguyên hay không. Ngay cả khi phát sóng, vẫn có thể giá trị hoặc một số giá trị trung gian trong một hoạt động hoặc biểu thức sẽ vượt quá giá trị tối đa hoặc tối thiểu đối với loại phát sóng và gây ra tràn, sẽ không được phát hiện và sẽ gây ra kết quả không mong muốn và không mong muốn. Thông qua phân tích hoặc các điều kiện trước, có thể ngăn chặn tràn với việc phát sóng khi việc ngăn chặn mà không phát sóng là không thể hoặc không thực tế. Nếu các số nguyên trong câu hỏi đã là các kiểu nguyên thủy dài, thì việc phát sóng không thể thực hiện được với các kiểu nguyên thủy trong Java.

Kỹ thuật BigInteger bao gồm sử dụng BigInteger cho hoạt động hoặc biểu thức số học bằng các phương thức thư viện sử dụng BigInteger. BigInteger không tràn. Nó sẽ sử dụng tất cả bộ nhớ có sẵn, nếu cần thiết. Các phương pháp số học của nó thường chỉ kém hiệu quả hơn một chút so với các phép toán số nguyên. Vẫn có thể kết quả sử dụng BigInteger có thể vượt quá giá trị tối đa hoặc tối thiểu cho một số nguyên, tuy nhiên, tràn sẽ không xảy ra trong số học dẫn đến kết quả. Lập trình và thiết kế vẫn sẽ cần xác định phải làm gì nếu kết quả BigInteger vượt quá giá trị tối đa hoặc tối thiểu cho loại kết quả nguyên thủy mong muốn, ví dụ: int hoặc long.

Chương trình CERT của Viện Kỹ thuật phần mềm Carnegie Mellon và Oracle đã tạo ra một bộ tiêu chuẩn để lập trình Java an toàn. Bao gồm trong các tiêu chuẩn là các kỹ thuật để ngăn chặn và phát hiện tràn số nguyên. Tiêu chuẩn được xuất bản dưới dạng tài nguyên trực tuyến có thể truy cập tự do tại đây: Tiêu chuẩn mã hóa bảo mật CERT Oracle cho Java

Phần tiêu chuẩn mô tả và chứa các ví dụ thực tế về kỹ thuật mã hóa để ngăn chặn hoặc phát hiện tràn số nguyên có tại đây: NUM00-J. Phát hiện hoặc ngăn chặn tràn số nguyên

Mẫu sách và mẫu PDF của Tiêu chuẩn mã hóa bảo mật Oracle CERT cho Java cũng có sẵn.


đây là câu trả lời tốt nhất ở đây vì nó nêu rõ những gì underflow là (câu trả lời được chấp nhận không) và cũng liệt kê các kỹ thuật để đối phó với tràn / underflow
gian giữa

12

Bản thân tôi cũng gặp phải vấn đề này, đây là giải pháp của tôi (cho cả phép nhân và phép cộng):

static boolean wouldOverflowOccurwhenMultiplying(int a, int b) {
    // If either a or b are Integer.MIN_VALUE, then multiplying by anything other than 0 or 1 will result in overflow
    if (a == 0 || b == 0) {
        return false;
    } else if (a > 0 && b > 0) { // both positive, non zero
        return a > Integer.MAX_VALUE / b;
    } else if (b < 0 && a < 0) { // both negative, non zero
        return a < Integer.MAX_VALUE / b;
    } else { // exactly one of a,b is negative and one is positive, neither are zero
        if (b > 0) { // this last if statements protects against Integer.MIN_VALUE / -1, which in itself causes overflow.
            return a < Integer.MIN_VALUE / b;
        } else { // a > 0
            return b < Integer.MIN_VALUE / a;
        }
    }
}

boolean wouldOverflowOccurWhenAdding(int a, int b) {
    if (a > 0 && b > 0) {
        return a > Integer.MAX_VALUE - b;
    } else if (a < 0 && b < 0) {
        return a < Integer.MIN_VALUE - b;
    }
    return false;
}

vui lòng sửa nếu sai hoặc nếu có thể được đơn giản hóa. Tôi đã thực hiện một số thử nghiệm với phương pháp nhân, chủ yếu là các trường hợp cạnh, nhưng nó vẫn có thể sai.


Phân chia có khả năng chậm so với phép nhân. Đối với int*int, tôi nghĩ đơn giản là chọn longvà xem liệu kết quả phù hợp intsẽ là cách tiếp cận nhanh nhất. Đối với long*long, nếu một người bình thường hóa các toán hạng là dương, thì người ta có thể chia từng nửa thành nửa trên và dưới 32 bit, quảng bá mỗi nửa thành dài (hãy cẩn thận về các phần mở rộng dấu!), Và sau đó tính hai sản phẩm một phần [một trong hai nửa trên bằng không].
supercat

Khi bạn nói "Trong một thời gian dài * dài, nếu một người bình thường hóa toán hạng thành tích cực ...", bạn sẽ làm thế nào để bình thường hóa Long.MIN_VALUE?
Fragorl

Các phương pháp này có thể thú vị nếu được yêu cầu kiểm tra nếu có thứ gì đó tràn ra trước khi thực sự thực hiện tính toán. Nó có thể tốt cho việc kiểm tra, ví dụ như đầu vào của người dùng được sử dụng cho các tính toán như vậy, thay vì bắt ngoại lệ khi nó xảy ra.
Maarten Bodewes

8

Có những thư viện cung cấp các hoạt động số học an toàn, kiểm tra tràn / tràn số nguyên. Ví dụ: IntMath.checkedAdd (int a, int b) của Guava trả về tổng của ab, miễn là nó không bị tràn và ném ArithmeticExceptionnếu a + btràn vào intsố học đã ký .


Đúng, đó là một ý tưởng tốt, trừ khi bạn là Java 8 trở lên, trong trường hợp đó Mathlớp chứa mã tương tự.
Maarten Bodewes

6

Nó quấn quanh.

ví dụ:

public class Test {

    public static void main(String[] args) {
        int i = Integer.MAX_VALUE;
        int j = Integer.MIN_VALUE;

        System.out.println(i+1);
        System.out.println(j-1);
    }
}

in

-2147483648
2147483647

Tốt! Và bây giờ, bạn có thể trả lời, làm thế nào để phát hiện nó thành phép tính phức tạp không?
Aubin

5

Tôi nghĩ bạn nên sử dụng một cái gì đó như thế này và nó được gọi là Upcasting:

public int multiplyBy2(int x) throws ArithmeticException {
    long result = 2 * (long) x;    
    if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE){
        throw new ArithmeticException("Integer overflow");
    }
    return (int) result;
}

Bạn có thể đọc thêm ở đây: Phát hiện hoặc ngăn chặn tràn số nguyên

Đây là nguồn khá đáng tin cậy.


3

Nó không làm gì cả - việc tràn / tràn chỉ xảy ra.

"-1" là kết quả của một tính toán tràn không khác với "-1" do bất kỳ thông tin nào khác. Vì vậy, bạn không thể thông qua một số trạng thái hoặc bằng cách kiểm tra chỉ một giá trị cho dù nó bị tràn.

Nhưng bạn có thể thông minh về các tính toán của mình để tránh tràn, nếu nó quan trọng, hoặc ít nhất là biết khi nào nó sẽ xảy ra. Tình hình của bạn thế nào


Đó không thực sự là một tình huống, chỉ là điều mà tôi tò mò và khiến tôi suy nghĩ. Nếu bạn cần một trường hợp sử dụng ví dụ, thì đây là: Tôi có một lớp với biến nội bộ của riêng nó được gọi là 'giây'. Tôi có hai phương thức lấy một số nguyên làm tham số và sẽ tăng hoặc giảm (tương ứng) 'giây' nhiều như vậy. Làm thế nào bạn có thể kiểm tra đơn vị rằng một dòng chảy / tràn đang xảy ra và làm thế nào bạn có thể ngăn chặn nó xảy ra?
KushalP

1
static final int safeAdd(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left > Integer.MAX_VALUE - right
                : left < Integer.MIN_VALUE - right) {
    throw new ArithmeticException("Integer overflow");
  }
  return left + right;
}

static final int safeSubtract(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left < Integer.MIN_VALUE + right
                : left > Integer.MAX_VALUE + right) {
    throw new ArithmeticException("Integer overflow");
  }
  return left - right;
}

static final int safeMultiply(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left > Integer.MAX_VALUE/right
                  || left < Integer.MIN_VALUE/right
                : (right < -1 ? left > Integer.MIN_VALUE/right
                                || left < Integer.MAX_VALUE/right
                              : right == -1
                                && left == Integer.MIN_VALUE) ) {
    throw new ArithmeticException("Integer overflow");
  }
  return left * right;
}

static final int safeDivide(int left, int right)
                 throws ArithmeticException {
  if ((left == Integer.MIN_VALUE) && (right == -1)) {
    throw new ArithmeticException("Integer overflow");
  }
  return left / right;
}

static final int safeNegate(int a) throws ArithmeticException {
  if (a == Integer.MIN_VALUE) {
    throw new ArithmeticException("Integer overflow");
  }
  return -a;
}
static final int safeAbs(int a) throws ArithmeticException {
  if (a == Integer.MIN_VALUE) {
    throw new ArithmeticException("Integer overflow");
  }
  return Math.abs(a);
}

2
Điều này xử lý thử nghiệm. Mặc dù không giải thích cách Java xử lý số nguyên tràn và tràn (thêm một số văn bản để giải thích).
Spencer Wieczorek

1

Tôi nghĩ rằng điều này sẽ ổn.

static boolean addWillOverFlow(int a, int b) {
    return (Integer.signum(a) == Integer.signum(b)) && 
            (Integer.signum(a) != Integer.signum(a+b)); 
}

0

Có một trường hợp, không được đề cập ở trên:

int res = 1;
while (res != 0) {
    res *= 2;

}
System.out.println(res);

sẽ sản xuất:

0

Trường hợp này đã được thảo luận ở đây: tràn số nguyên tạo ra Zero.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.