2010-09-24 59 views
5

Hãy xem xét các đoạn mã sau đây:Làm cách nào để printf và scanf xử lý các định dạng độ chính xác của dấu phẩy động?

float val1 = 214.20; 
double val2 = 214.20; 

printf("float : %f, %4.6f, %4.2f \n", val1, val1, val1); 
printf("double: %f, %4.6f, %4.2f \n", val2, val2, val2); 

Những kết quả đầu ra:

float : 214.199997, 214.199997, 214.20 | <- the correct value I wanted 
double: 214.200000, 214.200000, 214.20 | 

Tôi hiểu rằng 214.20 có một đại diện nhị phân vô hạn. Hai yếu tố đầu tiên của dòng đầu tiên có một xấp xỉ của giá trị dự định, nhưng người cuối cùng dường như không có xấp xỉ ở tất cả, và điều này dẫn tôi đến những câu dưới đây:

Làm thế nào để các scanf, fscanf, Các hàm printf, fprintf (vv) có xử lý các định dạng chính xác không?

Không có độ chính xác được cung cấp, printf in ra giá trị gần đúng, nhưng với %4.2f nó đã cho kết quả chính xác. Bạn có thể giải thích cho tôi thuật toán được sử dụng bởi các hàm này để xử lý độ chính xác không?

Trả lời

10

Điều này là 214.20 không thể được biểu thị chính xác chính xác với biểu diễn nhị phân. Vài số thập phân có thể. Vì vậy, một xấp xỉ được lưu trữ. Bây giờ khi bạn sử dụng printf, biểu diễn nhị phân được biến thành một biểu diễn thập phân, nhưng nó không thể được biểu diễn chính xác và chỉ xấp xỉ.

Khi bạn nhận thấy, bạn có thể đưa ra độ chính xác cho printf để cho biết cách làm tròn xấp xỉ thập phân. Và nếu bạn không cung cấp cho nó một độ chính xác sau đó một độ chính xác của 6 được giả định (xem trang người đàn ông để biết chi tiết).

Nếu bạn sử dụng %.40f cho phao và %.40lf cho đôi trong ví dụ của bạn ở trên, bạn sẽ nhận được những kết quả này:

214.1999969482421875000000000000000000000000 
214.1999999999999886313162278383970260620117 

Họ là khác nhau bởi vì với đôi, có nhiều bit để tốt hơn xấp xỉ 214,20. Nhưng như bạn có thể thấy, chúng vẫn rất kỳ quặc khi được biểu diễn bằng số thập phân.

Tôi khuyên bạn nên đọc Wikipedia article on floating point numbers để biết thêm thông tin chi tiết về cách các số dấu phẩy động hoạt động. Số đọc tuyệt vời cũng là What Every Computer Scientist Should Know About Floating-Point Arithmetic

+0

Cảm ơn bạn đã trả lời, mặc dù câu hỏi của tôi vẫn ở cùng một nơi. Ở vị trí đầu tiên, float không thể chứa giá trị chính xác 214.20, sau đó làm thế nào nó có thể lấy lại nó chỉ với độ chính xác% 4.2f và NOT với% 4.6f. Nó đã làm gì để lấy lại giá trị. Tôi hiểu rằng sự xấp xỉ được thực hiện, nhưng NHỮNG MẪU BIT ĐƯỢC SỬ DỤNG LÀ GÌ ĐƯỢC LƯU TRỮ INTERNIDY 214.199997 ĐỂ TRỞ THÀNH 214,20 VÀ LÀM THẾ NÀO ĐỂ CHÚNG TÔI ĐẾN NHƯ THẾ NÀO? –

+0

Không chắc chắn tôi có được bạn, nhưng đó chỉ là [làm tròn] (http://en.wikipedia.org/wiki/Rounding). Đó là cơ hội thuần túy mà '% 4.2f' cung cấp cho bạn chính xác giá trị mà bạn muốn. Nó vẫn "sai" vì biểu diễn nhị phân là * không * tương đương với 214,20 nhưng đến 214,1999969482421875. Bây giờ, nếu bạn làm tròn "214.1999969482421875" thành 6 chữ số, bạn nhận được "214.199997" bởi vì chữ số thứ 7 là số 9, biến số 6 thành số 7. Nhưng khi bạn chỉ tròn thành 2 chữ số, bạn sẽ nhận được "214,20" vì chữ số thứ 3 là chín, vì vậy bạn cần tăng số 9 trên số thứ hai. Điều đó "tràn" đến 10, chuyển "0,19" thành "0,20". – DarkDust

+0

Oh !! Tôi tin rằng bạn đang cố gắng để nói rằng hoạt động này được thực hiện trong biểu diễn thập phân, và đó là cách 214.199997 được hình thành như là một hiệu ứng tròn của độ chính xác gấp đôi. Bởi vì nó đã được trong đại diện nhị phân, chúng tôi đã xác nhận rằng 214,20 là không thể đại diện trong nhị phân, với tất cả các kỹ năng làm tròn được sử dụng. –

1

scanf sẽ làm tròn giá trị đầu vào thành giá trị dấu phẩy động chính xác gần nhất. Khi câu trả lời của DarkDust minh họa, trong độ chính xác đơn, giá trị chính xác gần nhất thấp hơn giá trị chính xác, và với độ chính xác gấp đôi, giá trị chính xác gần nhất nằm trên giá trị chính xác.

+0

Nếu có thể làm như vậy, tại sao chúng tôi lưu trữ xấp xỉ ở vị trí đầu tiên? :-). Xin lỗi nếu tôi gây đau đớn, nhưng đó là điều tôi muốn biết, scanf làm gì để đại diện cho một giá trị được đưa ra ở sáu vị trí sau thập phân để được chuyển đổi thành hai điểm sau thập phân. Cho rằng .20 KHÔNG đại diện trong dấu phẩy động nhị phân –

+0

Dường như câu hỏi tiếp theo này được trả lời trong hộp thoại bình luận với DarkDust. –

4

Vì bạn hỏi về scanf, có một điều bạn nên lưu ý là POSIX đòi hỏi printf và sau scanf (hoặc strtod) để tái tạo lại giá trị ban đầu chính xác miễn là đủ đáng kể chữ số (ít nhất DECIMAL_DIG, tôi tin) đã được in. Đồng bằng C tất nhiên không yêu cầu như vậy; các tiêu chuẩn C khá nhiều cho phép các hoạt động điểm nổi để cung cấp cho bất cứ điều gì kết quả mà người triển khai thích miễn là họ ghi lại nó. Tuy nhiên, nếu mục đích của bạn là lưu trữ số dấu chấm động trong các tệp văn bản được đọc lại sau, bạn có thể nên sử dụng công cụ chỉ định C99 %a để in chúng dưới dạng thập lục phân. Bằng cách này họ sẽ được chính xác và không có sự nhầm lẫn về việc liệu serialize/deserialize quá trình mất độ chính xác.Hãy nhớ rằng khi tôi nói "tái tạo lại giá trị ban đầu", tôi có nghĩa là giá trị thực tế được giữ trong biến/biểu thức được chuyển đến printf, không phải là số thập phân gốc bạn đã viết trong tệp nguồn được làm tròn thành đại diện nhị phân tốt nhất bởi trình biên dịch.

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