2016-09-13 15 views
5

Tôi đang sử dụng đoạn mã sau để chuyển đổi một thập lục phân String đến một điểm nổi String:Hexadecimal -> Float chuyển đổi không chính xác

private static String removeScientificNotation(float value) 
{ 
    return new BigDecimal(Float.toString(value)).toPlainString(); 
} 

/** 
* Converts a hexadecimal value to its single precision floating point representation 
* 
* @param hexadecimal The <code>hexadecimal</code> to convert 
* @return The converted value 
*/ 
public static String hexadecimalToFloatingPoint(String hexadecimal) 
{ 
    Long longBits = Long.parseLong(hexadecimal, 16); 
    Float floatValue = Float.intBitsToFloat(longBits.intValue()); 

    return removeScientificNotation(floatValue); 
} 

Để kiểm tra điều này, tôi đã viết JUnit thử nghiệm sau đây:

public class TestConversions 
{ 
    @Test 
    public void testConversions() 
    { 
     String floatValue = Conversions.hexadecimalToFloatingPoint("40000000"); 
     Assert.assertEquals(floatValue, "2.0"); 
     floatValue = Conversions.hexadecimalToFloatingPoint("50000000"); 
     Assert.assertEquals(floatValue, "8589934592"); 
     floatValue = Conversions.hexadecimalToFloatingPoint("C0000000"); 
     Assert.assertEquals(floatValue, "-2.0"); 
    } 
} 

Tuy nhiên, xác nhận thứ 2 không thành công. Theo nhiều người chuyển đổi trực tuyến khác nhau như this one, 50000000 phải được chuyển đổi thành 8589934592 nhưng Java trả lại 8589934600.

org.junit.ComparisonFailure: 
Expected :8589934600 
Actual :8589934592 

Kết quả nào là chính xác? Nếu Java sai thì làm thế nào để sửa nó?

+0

Tôi nghi ngờ những gì bạn thấy là chỉ có 7 chữ số chính xác đáng kể nên được dựa vào 'float' ... (Thử trả về' float' thay vì 'String' và so sánh kết quả của bạn với 8589934600f) –

+5

Lưu ý rằng * giá trị float chính xác gần nhất * tới 8589934600 là 8589934592. –

+0

Đó là lý do tại sao bạn không nên so sánh chính xác với phao nổi. Đối với các xác nhận trong các phép thử đơn vị, có 'Assert # equals (floatA, floatB, delta)'. –

Trả lời

5

Bạn có thể thực hiện điều này bằng cách truyền giá trị phao trực tiếp sang hàm dựng lớn BigDecimal như dưới đây. Nguyên nhân của sự cố là Float.toString(value) (Vì phương pháp này gọi nội bộ là FloatingDecimaltoJavaFormatString nó có vẻ làm tròn số giá trị) nên không cần sử dụng Float.toString thay vì chỉ truyền giá trị thực của bạn.

String myString = "50000000"; 
    Long i = Long.parseLong(myString, 16); 
    Float f = Float.intBitsToFloat(i.intValue()); 
    String finalString= new BigDecimal(f).toPlainString(); 
    System.out.println("final value "+finalString); 

Vì vậy, chỉ cần thay đổi phương thức của bạn như dưới đây.

private static String removeScientificNotation(float value) 
{ 
    return new BigDecimal(value).toPlainString(); 
} 
+0

Vấn đề cụ thể với 'Float.toString (giá trị)' là nó chọn, từ phạm vi số thập phân sẽ chuyển thành 'giá trị', số có số có nghĩa nhỏ nhất, chứ không phải giá trị chính xác. Tốt cho hiển thị, không tốt cho mục đích này. –

+0

Chỉ cần một lưu ý phụ, không cần sử dụng 'Long' và' Float' thay vì các loại 'long' và' float' thực tế. Gọi 'intValue()' trên một 'Long' chỉ làm xáo trộn kiểu truyền từ' long' thành 'int' xảy ra ở đây. Bên cạnh đó, nó chỉ kết hợp boxing không cần thiết và unboxing. – Holger

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