2013-07-21 29 views
7

khi tôi thực hiện 5.2 - 2.3 trong ghci, tôi sẽ nhận được 2.9000000000000004 thay vì 2.9. Cũng như vậy xấu xí (và cho một WRONG con người) kết quả cho thấy lên trên những nơi khác khi làm việc với đôi hoặc nổi.đôi khi xấu xí - tại sao lại là 2.9000000000000004 thay vì 2.9?

Tại sao điều này lại xảy ra? (điều này chỉ dành cho sự tò mò, không phải câu hỏi thực sự của tôi)

Câu hỏi thực sự của tôi: Làm cách nào để nói với Ghci không làm điều đó và hiển thị kết quả Hoạt động tăng gấp đôi cũng như bất kỳ ngôn ngữ lập trình nào khác (và máy tính) và cứ 15 tuổi thì sẽ viết chúng?

Điều này thật khó chịu khi tôi sử dụng ghci như một máy tính tốt và làm việc trên các danh sách mà tôi thực hiện các thao tác đó.

map ((-)2.3) [4.0, 3.8, 5.2, 6.4, 1.3, 8.3, 13.7, 9.0, 7.5, 2.4] 
[-1.7000000000000002,-1.5,-2.9000000000000004,-4.1000000000000005,0.9999999999999998,-6.000000000000001,-11.399999999999999,-6.7,-5.2,-0.10000000000000009] 

này chỉ không giúp khi sử dụng các con số trên một mảnh giấy sau đó

Cảm ơn trước :)

+3

giống như một bình luận phụ, đây có lẽ là câu hỏi thường được hỏi nhất trong lịch sử bao giờ hết. –

Trả lời

8

Làm cách nào để nói với Ghci không làm điều đó và hiển thị kết quả Hoạt động tăng gấp đôi giống như bất kỳ ngôn ngữ lập trình nào khác (và máy tính) và 15 năm tuổi sẽ viết chúng?

Từ những kết quả đó là kết quả GHCI thực tế (và máy tính tiêu chuẩn của bạn *) tính toán bạn không thể thay đổi đại diện nội bộ của kết quả (see TNI's answer). Vì bạn chỉ muốn hiển thị số thập phân cố định, đó là vấn đề của bản trình bày (so sánh với printf("%f.2",...) trong C).

Một giải pháp cho điều này có thể được tìm thấy trong https://stackoverflow.com/a/2327801/1139697. Nó có thể được áp dụng như thế này:

import Numeric 
fixedN :: (RealFloat b) => Int -> b -> String 
fixedN a b = showFFloat (Just a) b "" 

map (fixedN 2 . (-)2.3) [4.0, 3.8, 5.2, 6.4, 1.3, 8.3, 13.7, 9.0, 7.5, 2.4] 
-- result: ["-1.70","-1.50","-2.90","-4.10","1.00","-6.00",...] 

Lưu ý rằng điều này sẽ không khả thi nếu bạn muốn tiếp tục tính toán. Nếu bạn muốn chính xác số học, thì tốt hơn bạn nên sử dụng Rationals. Đừng quên rằng đầu vào của bạn nên hợp lý trong trường hợp này.

* có, ngay cả máy tính tiêu chuẩn của bạn cũng làm như vậy, lý do duy nhất bạn không nhìn thấy nó là bản trình bày cố định, nó không thể hiển thị nhiều hơn số thập phân cố định.

+1

Thực ra, nhiều máy tính bỏ túi đơn giản sử dụng [BCD] (http://en.wikipedia.org/wiki/Binary-coded_decimal), mà không bị vấn đề này nhưng hoàn toàn không phù hợp để làm bất cứ điều gì tiên tiến hơn so với rễ vuông hiệu quả. Bạn có thể nói nó phá vỡ lỗi trong cách đếm của con người ... – leftaroundabout

18

Tại sao điều này xảy ra?

Vì một số số điểm động nhất định không thể được biểu diễn bằng số bit hữu hạn mà không làm tròn. Floating-point numbers có số chữ số giới hạn, chúng không thể đại diện cho tất cả real numbers chính xác: khi có nhiều chữ số hơn định dạng cho phép, các chữ số còn lại bị bỏ qua - số được làm tròn.

Bạn có thể đọc What Every Computer Scientist Should Know About Floating-Point Arithmeticthis answer.

+2

thực sự họ thậm chí không thể đại diện cho tất cả các lý do như chúng ta thấy ở đây –

3

Đó là bản chất của các số dấu phẩy động mà chúng không thể đại diện chính xác số thực (cũng không hợp lý). Chuyển đổi mặc định của Haskell thành chuỗi đảm bảo rằng khi số được đọc lại, bạn nhận được chính xác cùng một biểu diễn.Nếu bạn muốn có một cách khác để in các số, bạn có thể tạo một kiểu riêng cho thấy số khác nhau.

Giống như (chưa được kiểm tra):

newtype MyDouble = MyDouble {getMyDouble :: Double} 
      deriving (Eq, Ord, Num, Real, RealFrac, Fractional, Floating) 
instance Show MyDouble where show = printf "%g" . getMyDouble 
default (MyDouble) 

Điều này tạo ra một bản sao của loại Double, nhưng với một Show dụ khác nhau mà chỉ in một vài thập phân. Tuyên bố default làm cho trình biên dịch chọn loại này khi có sự mơ hồ. Oh, và để thực hiện công việc này, bạn cần một vài phần mở rộng ngôn ngữ.

Bạn cũng có thể thử loại CReal từ gói numbers.

3

Làm thế nào để nói với ghci để không làm điều đó, và hiển thị các kết quả của hoạt động trên đôi cũng giống như bất kỳ ngôn ngữ khác lập trình (và máy tính) sẽ

Bạn có thực sự cố gắng "bất kỳ khác ngôn ngữ lập trình"? Hay là bạn vừa mới bắt nạt?

FWIW, đây là sản phẩm của một thông dịch viên cho một ngôn ngữ sử dụng JVM:

frege> 5.2 - 2.3 
2.9000000000000004 

Trông với tôi như thể bạn sẽ nhận được rằng kết quả tương tự với tất cả các ngôn ngữ JVM. Và vì JVM được viết bằng C++, rất có thể là kết quả sẽ giống nhau. Và vì hầu hết các ngôn ngữ đều được viết bằng C/C++, rất có thể bạn sẽ nhận được kết quả rất giống với các ngôn ngữ đó. Trừ khi họ rất "thân thiện với người dùng" và thực hiện làm tròn mà bạn không yêu cầu.

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