Bạn có thể so sánh nó với Long.MIN_VALUE
và Long.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
và -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
Đố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
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
@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 –