2015-09-08 27 views
7

Tôi nghĩ câu hỏi đó khá thẳng. nhưng đây là một ví dụ.Làm thế nào để kiểm tra nếu giá trị được lưu trữ trong phù hợp với đôi trong dài? (làm tròn có, nhưng cắt ngắn không)

Ví dụ bên dưới là OK. Tôi có thể làm tròn và không cắt xén được thực hiện ở đây.

public static void main(String[] args) { 
    double d = 9.9; 
    long l = (long)d; 
    System.out.println(l); 
} 

Output:

9 

Và bây giờ số ra khỏi phạm vi dài:

public static void main(String[] args) { 
    double d = 99999999999999999999999999999999.9; 
    long l = (long)d; 
    System.out.println(l); 
} 

Output:

9223372036854775807 

Cái này phiền tôi. Tôi không thể tiếp tục làm việc với số lượng hoàn toàn khác. Tôi thà nhận được một lỗi hoặc một ngoại lệ.

Có cách nào để phát hiện điều này trong Java không?

Trả lời

12

Bạn có thể so sánh nó với Long.MIN_VALUELong.MAX_VALUE:

public static boolean fitsLong(double d) { 
    return d >= Long.MIN_VALUE && d < Long.MAX_VALUE; 
} 

cách tiếp cận Hơi sofisticated hơn là sử dụng BigDecimal:

double value = 1234567.9; 
long l = BigDecimal.valueOf(value) 
        .setScale(0, RoundingMode.HALF_EVEN) 
        .longValueExact(); // 1234568 

double value = 99999999999999999999999999999999.9; 
long l = BigDecimal.valueOf(value) 
        .setScale(0, RoundingMode.HALF_EVEN) 
        .longValueExact(); // ArithmeticException 

Bằng cách này bạn có thể kiểm soát cách làm tròn được thực hiện.

Bạn có thể hỏi, tại sao có sự bất bình đẳng nghiêm ngặt trong fitsLong: d < Long.MAX_VALUE. Thực ra đó là vì bản thân số Long.MAX_VALUE không thể được biểu diễn dưới dạng số kép. Khi bạn truyền (double)Long.MAX_VALUE, không có đủ độ chính xác ở loại double để đại diện cho nó, do đó, giá trị có thể biểu diễn gần nhất được chọn là 9223372036854775808.0 (Long_MAX_VALUE+1.0). Vì vậy, nó là d <= Long.MAX_VALUE nó sẽ trả lại true cho số thực sự lớn hơn một chút như trong so sánh này hằng số Long.MAX_VALUE được quảng bá thành loại kép. Mặt khác, Long.MIN_VALUE có thể được biểu diễn chính xác theo loại double, do đó ở đây chúng tôi có >=.

Cũng thật thú vị tại sao các công việc sau:

double value = -9223372036854775809.9; // Long.MIN_VALUE-1.9 
System.out.println(fitsLong(value)); // returns true 

Đó là bởi vì bạn thực sự không trừ bất cứ điều gì từ Long.MIN_VALUE. Xem:

double d1 = Long.MIN_VALUE; 
double d2 = -9223372036854775809.9; 
System.out.println(d1 == d2); // true 

Độ chính xác đôi là không đủ để phân biệt giữa -9223372036854775808-9223372036854775809.9, do đó, nó thực sự là số đôi cùng. Trong quá trình biên dịch, nó được chuyển thành dạng nhị phân và dạng nhị phân cho hai số này giống nhau. Vì vậy, có chương trình biên dịch, bạn không thể phân biệt được -9223372036854775808 hoặc -9223372036854775809.9 có trong mã nguồn hay không.

Nếu bạn cảm thấy rằng nó vẫn còn vấn đề, xây dựng BigDecimal từ String:

long l = new BigDecimal("-9223372036854775808.2") 
        .setScale(0, RoundingMode.HALF_EVEN) 
        .longValueExact(); // ok, -9223372036854775808 

long l = new BigDecimal("-9223372036854775808.9") 
        .setScale(0, RoundingMode.HALF_EVEN) 
        .longValueExact(); // ArithmeticException 
+0

Đối với giải pháp đầu tiên) Trên thực tế bạn không thể. Tôi đã thử nó và nó sẽ không hoạt động. Ví dụ: hãy thử một giá trị 9223372036854775809.9 được tăng dài.MAX_VALUE lên 2.9. Nó sẽ vượt qua. – arenaq

+0

Bạn nói đúng, nhưng khi tôi sử dụng 9223372036854775807.0 cho cả hai giải pháp. FitsLong mang lại cho tôi sự thật và BigDecimal ném một ngoại lệ. Giá trị gấp đôi đó có thể không thay đổi, nhưng ít nhất một trong các giải pháp phải sai. – arenaq

+1

@arenaq double có 53 bit mantissa và có thể chính xác chỉ ~ 15-17 chữ số. Không có cách nào để phân biệt giữa 9223372036854775809.9 và 9223372036854775807 http://stackoverflow.com/q/588004/995714 –

1

Khi bạn cast một loại dấu chấm cho một int hay long, kết quả là một trong hai số nguyên gần nhất (làm tròn về phía số không) hoặc MIN_VALUE hoặc MAX_VALUE cho int hoặc long. Xem JLS 5.1.3.

Do đó, một giải pháp thay thế sẽ là thực hiện việc định kiểu và sau đó kiểm tra MIN_VALUE hoặc MAX_VALUE thích hợp.

Lưu ý rằng Long.MAX_VALUE9223372036854775807 ... là số mà chương trình thử nghiệm của bạn xuất ra!

(Tuy nhiên, điều này không có tác dụng nếu bạn đang đúc một loại dấu chấm để byte, char hoặc short. Xem liên kết ở trên để giải thích.)

Các vấn đề liên quan