Câu hỏi Chuyển đổi float để tăng gấp đôi mà không làm mất chính xác


Tôi có một phao nguyên thủy và tôi cần phải là một đôi nguyên thủy. Đơn giản chỉ cần đúc phao để tăng gấp đôi mang lại cho tôi thêm độ chính xác kỳ lạ. Ví dụ:

float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375

Tuy nhiên, nếu thay vì đúc, tôi xuất ra phao như một chuỗi, và phân tích cú pháp chuỗi như là một đôi, tôi nhận được những gì tôi muốn:

System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35

Có cách nào tốt hơn để đi đến String và trở lại?


76
2018-05-27 14:39


gốc




Các câu trả lời:


Nó không phải là bạn thực ra nhận được độ chính xác cao hơn - đó là phao không đại diện chính xác số bạn đang nhắm đến ban đầu. Đôi  đại diện cho phao gốc chính xác; toString đang hiển thị dữ liệu "phụ" đã có.

Ví dụ (và những con số này không đúng, tôi chỉ làm mọi thứ) hỗ trợ bạn có:

float f = 0.1F;
double d = f;

Sau đó, giá trị của f có thể chính xác là 0.100000234523. d sẽ có chính xác cùng giá trị, nhưng khi bạn chuyển đổi nó thành một chuỗi, nó sẽ "tin tưởng" rằng nó chính xác đến độ chính xác cao hơn, vì vậy sẽ không làm tròn sớm, và bạn sẽ thấy "số bổ sung" đã có, nhưng ẩn từ bạn.

Khi bạn chuyển đổi thành chuỗi và quay lại, bạn sẽ kết thúc bằng một giá trị gấp đôi gần với giá trị chuỗi hơn so với giá trị ban đầu - nhưng điều đó chỉ tốt nếu bạn thực sự tin rằng giá trị chuỗi là những gì bạn thực sự muốn.

Bạn có chắc rằng float / double là các kiểu thích hợp để sử dụng ở đây thay vì BigDecimal? Nếu bạn đang cố gắng sử dụng các số có giá trị thập phân chính xác (ví dụ: tiền), thì BigDecimal là loại IMO phù hợp hơn.


103
2018-05-27 14:42



Tôi nghĩ câu trả lời của bạn mô tả tốt nhất lý do cho vấn đề của tôi. Giải pháp của tôi có thể 'làm việc' nhưng tôi nhận ra bây giờ tôi sắp hỏi câu hỏi sai. Cảm ơn. - Steve Armstrong
Tôi đã có cùng một vấn đề trong .NET và điều này giải thích nó rất tốt, cảm ơn bạn. - EMP


Tôi thấy việc chuyển đổi sang biểu diễn nhị phân dễ dàng hơn để nắm bắt vấn đề này.

float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;

System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));

Bạn có thể thấy phao được mở rộng đến gấp đôi bằng cách thêm 0 vào cuối, nhưng biểu diễn kép của 0,27 là 'chính xác hơn', do đó vấn đề.

   111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000

32
2018-02-07 08:45





Điều này là do hợp đồng Float.toString(float), mà nói một phần:

Phải in bao nhiêu chữ số cho   phần phân đoạn […]? Ở đó   phải có ít nhất một chữ số để   đại diện cho phần phân đoạn và   hơn thế nữa, nhưng chỉ nhiều, cần nhiều chữ số hơn để duy nhất   phân biệt giá trị đối số từ   các giá trị liền kề của kiểu float. Cái đó   là, giả sử rằng x là chính xác   giá trị toán học được đại diện bởi   đại diện thập phân được tạo ra bởi   phương pháp này cho một nonzero hữu hạn   đối số f. Sau đó f phải là phao   giá trị gần nhất với x; hoặc, nếu hai phao   giá trị bằng nhau gần x, rồi f   phải là một trong số họ và ít nhất   một chút ý nghĩa và   f phải bằng 0.


23
2018-05-27 14:56



+1 Câu trả lời tuyệt vời với trích dẫn từ tài liệu. - EMP


Tôi đã gặp phải vấn đề này ngày hôm nay và không thể sử dụng refactor để BigDecimal, bởi vì dự án là rất lớn. Tuy nhiên tôi tìm thấy giải pháp sử dụng

Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()

Và điều này hoạt động.

Lưu ý rằng gọi result.doubleValue () trả về 5623.22998046875

Nhưng gọi doubleResult.doubleValue () trả về chính xác 5623.23

Nhưng tôi không hoàn toàn chắc chắn nếu nó là một giải pháp đúng.


14
2018-01-02 12:24



Đã làm cho tôi. Cũng rất nhanh. Không chắc chắn tại sao điều này không được đánh dấu là câu trả lời. - Sameer
Đây là phương pháp tôi sử dụng, và u không cần phần Float mới ... Chỉ cần tạo FloatingDecimal với nguyên thủy. Nó sẽ được tự động đóng hộp ... Và hoạt động nhanh quá ... Có một, một nhược điểm, FloatingDecimal là bất biến, vì vậy bạn cần tạo một cái cho mỗi phao đơn ... hãy tưởng tượng một mã tính toán của O (1e10)! !! Tất nhiên BigDecimal có cùng nhược điểm ... - Mostafa Zeinali
Hạn chế của giải pháp này là sun.misc.FloatingDecimal là lớp nội bộ của JVM và chữ ký của hàm tạo của nó đã được thay đổi trong Java 1.8. Không ai nên sử dụng các lớp bên trong trong ứng dụng thực tế. - Igor Bljahhin


Sử dụng một BigDecimalthay vì float/double. Có rất nhiều số không thể được biểu diễn dưới dạng dấu phẩy động nhị phân (ví dụ: 0.1). Vì vậy, bạn phải luôn luôn làm tròn kết quả đến độ chính xác hoặc sử dụng đã biết BigDecimal.

Xem http://en.wikipedia.org/wiki/Floating_point để biết thêm thông tin.


6
2018-05-27 14:45



Cho phép nói rằng bạn có 10 triệu giá thương mại nổi trong bộ nhớ cache của bạn, bạn vẫn xem xét việc sử dụng BigDecimal và khởi tạo độc đáo cho phép nói ~ 6m đối tượng (để giảm cân bay hơi / không thay đổi) .. hoặc có nhiều hơn? - Sendi_t
@Sendi_t Vui lòng không sử dụng bình luận để đặt câu hỏi phức tạp :-) - Aaron Digulla
Cảm ơn vì đã tìm kiếm! .. chúng tôi sử dụng phao ngay bây giờ như đã được thỏa thuận một thập kỷ trước - tôi đã cố gắng để xem quan điểm của bạn về tình hình này theo dõi -. cảm ơn! - Sendi_t
@Sendi_t Hiểu lầm. Tôi không thể trả lời câu hỏi của bạn trong 512 ký tự. Hãy hỏi một câu hỏi thích hợp và gửi cho tôi một liên kết. - Aaron Digulla
@GKFX Không có "một kích thước phù hợp với tất cả" khi nói đến xử lý số thập phân trên máy tính. Nhưng vì hầu hết mọi người không hiểu, tôi chỉ cho họ BigDecimal vì điều đó sẽ bắt được hầu hết các lỗi phổ biến. Nếu mọi thứ quá chậm, họ cần phải tìm hiểu thêm và tìm cách để tối ưu hóa vấn đề của họ. Tối ưu hóa sớm là gốc rễ của mọi điều ác - D.E. Knuth. - Aaron Digulla


Tôi tìm thấy giải pháp sau:

public static Double getFloatAsDouble(Float fValue) {
    return Double.valueOf(fValue.toString());
}

Nếu bạn dùng phao và gấp đôithay vì Phao và Gấp đôi sử dụng như sau:

public static double getFloatAsDouble(float value) {
    return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}

4
2018-01-25 16:20





Floats, theo bản chất, là không chính xác và luôn luôn có gọn gàng làm tròn "vấn đề". Nếu độ chính xác là quan trọng thì bạn có thể xem xét tái cấu trúc ứng dụng của mình để sử dụng thập phân hoặc BigDecimal.

Có, nổi được tính toán nhanh hơn so với số thập phân vì hỗ trợ bộ xử lý trên. Tuy nhiên, bạn có muốn nhanh hay chính xác?


1
2018-05-27 14:55



Số học thập phân cũng không chính xác. (ví dụ: 1/3 * 3 == 0,9999999999999999999999999999) Tất nhiên, tốt hơn là nên thể hiện chính xác số thập phân số lượng như tiền, nhưng đối với các phép đo vật lý, nó không có lợi thế. - dan04
Nhưng 1 == 0,9999999999999999999999999999 :) - Emmanuel Bourg


Thông tin này xuất hiện trong Mục 48 - Tránh nổi và tăng gấp đôi khi các giá trị chính xác được yêu cầu, của phiên bản hiệu quả Java thứ 2 của Joshua Bloch. Cuốn sách này là mứt đóng gói với những thứ tốt và chắc chắn đáng xem.


0
2018-05-27 15:05





Điều này có hiệu quả không?

float flt = 145.664454;

Double dbl = 0.0;
dbl += flt;

0
2018-03-04 12:18