2015-04-23 22 views
5

Tôi đang đọc từ một tệp JSON sử dụng jsoncpp. Khi tôi viết lại tập tin, các giá trị float của tôi hơi lệch. Để kiểm tra, tôi quyết định phân tích cú pháp tệp thành giá trị Json :: Value và sau đó viết giá trị đó trở lại tệp. Tôi hy vọng nó trông giống nhau, nhưng thay vào đó các giá trị float khác nhau.Jsoncpp viết các giá trị float không chính xác

Ví dụ:

"Parameters": 
{ 
"MinXValue": 0.1, 
"MaxXValue": 0.15, 
"MinYValue": 0.25, 
"MaxYValue": 1.1, 
"MinObjectSizeValue": 1 
} 

viết như:

"Parameters": 
{ 
"MinXValue": 0.10000000000000001, 
"MaxXValue": 0.14999999999999999, 
"MinYValue": 0.25, 
"MaxYValue": 1.1000000238418579, 
"MinObjectSizeValue": 1 
} 

Bạn có thể nhận thấy rằng 0,25 không thay đổi, mặc dù tất cả các phao nổi khác đã làm. Bất kỳ ý tưởng gì đang xảy ra ở đây?

+0

Một số giá trị điểm động có thể được biểu diễn chính xác theo dạng nhị phân và một số giá trị không thể. Những gì bạn đang thấy là đại diện gần nhất về giá trị của bạn. –

+0

Số dấu chấm động không chính xác. Họ là những đại diện tốt nhất trong bộ nhớ hạn chế. PS 0,25 là một phần tư - triệu tập để làm với làm việc trong nhị phân ;-) –

+0

Cảm ơn bạn đã làm rõ. Có cách nào để tránh điều này? – SFBA26

Trả lời

2

Đây thực sự là vấn đề về triển khai phân tích cú pháp/in số điểm nổi. Mặc dù số dấu phẩy động chỉ có thể biểu diễn một số số thập phân chính xác (0,25 là một trong số ~ 2^64), cần phân tích một biểu diễn chuỗi để biểu diễn nhị phân gần nhất. Khi in dấu chấm động, nó cũng cần thiết để in biểu diễn chuỗi (tốt nhất là ngắn nhất) có thể được phục hồi về biểu diễn nhị phân.

Tôi thừa nhận rằng tôi đã không điều tra JsonCPP để xem liệu có giải pháp cho việc này hay không. Nhưng như tôi là tác giả của RapidJSON, tôi đã cố gắng để xem cách RapidJSON thực hiện cho việc này:

const char json[] = 
    "{" 
    "\"MinXValue\": 0.1," 
    "\"MaxXValue\": 0.15," 
    "\"MinYValue\": 0.25," 
    "\"MaxYValue\": 1.1," 
    "\"MinObjectSizeValue\": 1" 
    "}"; 

using namespace rapidjson; 

Document d; 
d.Parse(json); 

StringBuffer sb; 
PrettyWriter<StringBuffer> writer(sb); 
d.Accept(writer); 

std::cout << sb.GetString(); 

Và kết quả:

{ 
    "MinXValue": 0.1, 
    "MaxXValue": 0.15, 
    "MinYValue": 0.25, 
    "MaxYValue": 1.1, 
    "MinObjectSizeValue": 1 
} 

RapidJSON thực hiện cả hai thuật toán phân tích và in nội bộ. Phân tích cú pháp chính xác thông thường sẽ có tối đa 3 lỗi ULP, nhưng với cờ phân tích chính xác đầy đủ (kParseFullPrecisionFlag) nó luôn có thể phân tích cú pháp để biểu diễn gần nhất. Phần in đã thực hiện thuật toán Grisu2. Nó luôn tạo ra kết quả chính xác và hơn 99% thời gian ngắn nhất (tối ưu).

Thực tế, sử dụng strtod()sprintf(..., "%.17g", ...) cũng có thể giải quyết vấn đề này. Nhưng chúng chậm hơn nhiều so với thư viện chuẩn C/C++ hiện tại. Ví dụ: tôi đã thực hiện một benchmark để in double. Vì vậy, trong RapidJSON, chúng tôi đã triển khai các giải pháp chỉ tiêu đề được tối ưu hóa của riêng mình.

+3

Cảm ơn bạn đã phản hồi. Thật không may, tôi đã không viết mã gốc và đã quá muộn để chuyển sang RapidJSON ngay bây giờ. Tôi đã thử sử dụng to_string để chuyển đổi các float trước khi ghi vào JSON, nhưng tôi đã gặp lỗi khi đọc các giá trị đã lưu bằng cách sử dụng .AsFloat(). Vì vậy, tôi nghĩ rằng lựa chọn duy nhất của tôi là chuyển đổi thành chuỗi khi viết và đọc dưới dạng chuỗi, sau đó chuyển thành float. – SFBA26

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