Làm thế nào để biết BigDecimal có thể chuyển đổi chính xác thành float hay double không?


10

Lớp BigDecimalcó một số phương thức hữu ích để đảm bảo chuyển đổi lossless:

  • byteValueExact()
  • shortValueExact()
  • intValueExact()
  • longValueExact()

Tuy nhiên, phương pháp floatValueExact()doubleValueExact()không tồn tại.

Tôi đọc mã nguồn OpenJDK cho các phương thức floatValue()doubleValue(). Cả hai dường như dự phòng Float.parseFloat()Double.parseDouble()tương ứng, có thể trả về vô cực dương hoặc âm. Ví dụ: phân tích chuỗi 10.000 9 giây sẽ trả về vô cực dương. Theo tôi hiểu, BigDecimalkhông có một khái niệm nội bộ về vô cực. Hơn nữa, phân tích một chuỗi 100 9s như doublecho 1.0E100, mà không phải là vô tận, nhưng mất độ chính xác.

Một thực hiện hợp lý là gì floatValueExact()doubleValueExact()?

Tôi nghĩ về một doublegiải pháp bằng cách kết hợp BigDecimal.doubleValue(), BigDecial.toString(), Double.parseDouble(String)Double.toString(double), nhưng có vẻ lộn xộn. Tôi muốn hỏi ở đây vì có thể (phải!) Là một giải pháp đơn giản hơn.

Để rõ ràng, tôi không cần một giải pháp hiệu suất cao.


7
Tôi đoán bạn có thể chuyển đổi thành gấp đôi, sau đó chuyển đổi gấp đôi thành BigDecimal và xem bạn có nhận được cùng một giá trị không. Không chắc chắn trường hợp sử dụng là gì mặc dù. Nếu bạn sử dụng gấp đôi, bạn đã chấp nhận có các giá trị không chính xác. Nếu bạn muốn các giá trị chính xác, thì hãy ở lại với BigDecimal.
JB Nizet

Câu trả lời:


6

Từ đọc các tài liệu , tất cả nó với các numTypeValueExactbiến thể là để kiểm tra sự tồn tại của một bộ phận phần hoặc nếu giá trị là quá lớn đối với loại số và ném ngoại lệ.

Đối với floatValue()doubleValue(), một kiểm tra tràn tương tự đang được thực hiện, nhưng thay vì ném một ngoại lệ, thay vào đó, nó trả về Double.POSITIVE_INFINITYhoặc Double.NEGATIVE_INFINITYcho đôi và Float.POSITIVE_INFINITYhoặc Float.NEGATIVE_INFINITYcho phao.

Do đó, việc triển khai hợp lý (và đơn giản nhất) các exactphương thức cho float và double, chỉ cần kiểm tra xem chuyển đổi có trả về POSITIVE_INFINITYhay không NEGATIVE_INFINITY.


Hơn nữa , hãy nhớ rằng nó BigDecimalđược thiết kế để xử lý sự thiếu chính xác đến từ việc sử dụng floathoặc doublecho những bất hợp lý lớn, do đó, như @JB Nizet đã nhận xét , một kiểm tra khác bạn có thể thêm vào ở trên sẽ là chuyển đổi doublehoặc floatquay lại BigDecimalđể xem bạn có còn nhận được không cùng giá trị. Điều này sẽ chứng minh việc chuyển đổi là chính xác.

Đây là những gì một phương pháp như vậy sẽ cho floatValueExact():

public static float floatValueExact(BigDecimal decimal) {
    float result = decimal.floatValue();
    if (!Float.isInfinite(result)) {
        if (new BigDecimal(String.valueOf(result)).compareTo(decimal) == 0) {
            return result;
        }
    }
    throw new ArithmeticException(String.format("%s: Cannot be represented as float", decimal));
}

Việc sử dụng compareTothay vì equalsở trên là có chủ ý để không trở nên quá nghiêm ngặt với việc kiểm tra. equalssẽ chỉ đánh giá là đúng khi hai BigDecimalđối tượng có cùng giá trị và tỷ lệ (kích thước của phần phân số của số thập phân), trong khi compareTosẽ bỏ qua sự khác biệt này khi nó không quan trọng. Ví dụ 2.0so với 2.00.


Rất xảo quyệt. Chính xác những gì tôi cần! Lưu ý: Bạn cũng có thể muốn sử dụng Float.isFinite()hoặc Float.isInfinite(), nhưng đây là tùy chọn. :)
kevinarpe

3
Bạn cũng nên thích so sánh bằng hơn, bởi vì bằng () trong BigDecimal sẽ coi 2.0 và 2.00 là các giá trị khác nhau.
JB Nizet

Các giá trị không thể được biểu diễn chính xác là float sẽ không hoạt động. Ví dụ : 123.456f. Tôi đoán điều này là do kích thước khác nhau có ý nghĩa (mantissa) giữa float 32 bit và double 64 bit. Trong mã trên của bạn, tôi nhận được kết quả tốt hơn với : if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {. Đây có phải là một thay đổi hợp lý ... hay tôi đang thiếu một trường hợp góc khác của các giá trị dấu phẩy động?
kevinarpe

1
@kevinarpe - 123.456fvề mặt khái niệm giống như ví dụ 1.0E100 của bạn. Tôi nhận ra rằng nếu bạn cần kiểm tra xem chuyển đổi từ BigDecimal -> dấu phẩy động nhị phân có chính xác không thì nhu cầu đó chính là vấn đề; tức là việc chuyển đổi không nên được suy ngẫm.
Stephen C
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.