2012-03-22 23 views
12

GNU bash, phiên bản 4.2.24:Strange "một nửa thậm chí" làm tròn bằng các ngôn ngữ khác nhau

$> printf "%.0f, %.0f\n" 48.5 49.5 
48, 50 

của Ruby 1.8.7

> printf("%.0f, %.0f\n", 48.5, 49.5) 
48, 50 

Perl 5.12.4

$> perl -e 'printf("%.0f, %.0f\n", 48.5, 49.5)' 
48, 50 

gcc 4.5.3:

> printf("%.0f, %.0f\n", 48.5, 49.5); 
48, 50 

GHC, phiên bản 7.0.4:

> printf "%.0f, %.0f\n" 48.5 49.5 
49, 50 

Wikipedia nói rằng loại tròn được gọi là round half to even:

Đây là mặc định chế độ sử dụng trong IEEE 754 chức năng tính toán và các nhà khai thác làm tròn .

Tại sao làm tròn này được sử dụng theo mặc định trong C, Perl, Ruby và bash, nhưng không có trong Haskell?

Đây có phải là một số loại truyền thống hoặc tiêu chuẩn không? Và nếu nó là một tiêu chuẩn, tại sao nó được sử dụng bởi những ngôn ngữ đó và không được sử dụng bởi Haskell? Một điểm làm tròn nửa là gì?

+4

Tôi nghĩ bạn vừa trả lời câu hỏi của riêng bạn. Đó là một phần của tiêu chuẩn IEEE 754. –

+0

@Keith Irwin, tôi tin rằng anh ấy muốn biết tiêu chuẩn nào. Tại sao bạn không đăng câu trả lời đó, hy vọng với một liên kết có liên quan? – ikegami

+2

Dmitry: Câu hỏi của bạn đặc biệt đề cập đến "IEEE 754". Đó là tiêu chuẩn. – Gabe

Trả lời

11
GHCi> round 48.5 
48 
GHCi> round 49.5 
50 

Sự khác biệt duy nhất là printf không sử dụng round - có lẽ vì nó có để có thể làm tròn đến nhiều hơn chỉ là toàn bộ số nguyên. Tôi không nghĩ IEEE 754 chỉ định bất cứ điều gì về cách thực hiện các chức năng định dạng kiểu dáng printf, chỉ làm tròn mà Haskell thực hiện chính xác.

Nó có lẽ sẽ là tốt nhất nếu printf nhất quán với round và triển khai các ngôn ngữ khác, nhưng tôi không nghĩ nó thực sự là một vấn đề lớn.

+0

chức năng định dạng kiểu printf chuyển đổi sang định dạng thập phân điểm cố định. IEEE 754 khuyến cáo rằng các chuyển đổi được thực hiện ở chế độ làm tròn hiện tại, gần nhất là mặc định. Lưu ý rằng nhiều ngôn ngữ/nền tảng vẫn nhận được lỗi này vào năm 2012, sử dụng gần nhất cho các chuyển đổi bất kể chế độ làm tròn hiện tại. –

+5

@PascalCuoq: Cảm ơn thông tin! Tuy nhiên, Haskell chỉ đơn giản là không thể thực hiện theo đề xuất đó, vì 'printf' phải là một hàm thuần túy. Nó sẽ phải thực hiện chế độ làm tròn làm tham số hoặc hoạt động trong ngữ cảnh (ví dụ: monadic), nơi thông tin đó có sẵn. – ehird

+0

Điểm tốt. Các tiêu chuẩn bất khả tri về ngôn ngữ như IEEE 754 để lại rất nhiều sự mơ hồ và có thể bất tiện khi thích ứng. Một tình huống tương tự tương tự khác là kiểm tra Hoare-logic các chương trình điểm nổi bắt buộc, nơi bạn muốn tránh suy nghĩ về chế độ làm tròn như một phần bổ sung của trạng thái và đối số của chương trình cho tất cả các hàm dấu phẩy động, nhưng bạn phải xử lý các chương trình sửa đổi nó tại thời gian chạy. –

2

Tôi không thể nói chắc chắn, nhưng điều này có thể phải làm với thực tế là loại làm tròn này thường được sử dụng trong các chức năng kế toán, vì điều này còn được gọi là làm tròn của ngân hàng. Nếu bạn nhìn xa hơn bài viết trên Wikipedia về làm tròn, bạn cũng sẽ nhận thấy điều này là mặc định trong IEEE 754, vì vậy khả năng Haskell không tuân theo tiêu chuẩn đó.

+1

Các bài báo liên kết của tiểu bang Wiki tuyên bố rằng "Nguồn gốc của vòng tròn ngân hàng hạn chế vẫn còn mơ hồ hơn. Nếu phương pháp làm tròn này đã từng là một tiêu chuẩn trong ngành ngân hàng, bằng chứng đã trở nên cực kỳ khó tìm thấy. Ngược lại, phần 2 của châu Âu Báo cáo của Ủy ban Việc giới thiệu Euro và làm tròn số tiền tệ [16] cho thấy trước đây không có cách tiếp cận chuẩn để làm tròn trong ngân hàng, và nó chỉ ra rằng số tiền "nửa đường" phải được __rounded up__. " Vì vậy, các ngân hàng __don't use__ của ngân hàng làm tròn. –

+0

@ A.H. Trên thực tế, họ vẫn sử dụng làm tròn của ngân hàng đến một mức độ hạn chế. Tôi có thể nói rằng với kiến ​​thức, như tôi đã từng làm việc cho một nhóm ngân hàng. –

+0

Tôi có kiến ​​thức đầu tiên về làm tròn được sử dụng trong các tổ chức tài chính. Nhưng không phải ở Mỹ. Nhưng khán giả ở đây cũng rất quốc tế. –

6

"Round to even" là mặc định để sử dụng với IEEE 754. Haskell có lẽ nên chuyển sang sử dụng nó trong printf vì lý do nhất quán. Dòng tương ứng của mã là trong GHC.Float

f 0 (x:_) = (if x >= b2 then 1 else 0, []) 

Vì vậy, nếu ai đó muốn sửa chữa nó, họ có thể. Như ehird chỉ ra, điều này sẽ chỉ làm cho chức năng roundTo đang được sử dụng bởi printf phù hợp với round mặc dù tôi không chắc chắn mã khác thay đổi này sẽ phá vỡ.

EDIT: phiên bản trước của câu trả lời này có vị trí của mã làm tròn sai. Sự khác biệt đáng kể duy nhất giữa hai lần triển khai là nếu chúng được mã hóa cứng để sử dụng cơ sở 10.

+2

Tôi không nghĩ đó là dòng mã đúng. 'Text.Printf' hoạt động với' Chuỗi', không phải 'Văn bản'. – ehird

+0

@ehird Text.Printf gọi một hàm trợ giúp gọi là 'dfmt'' gọi một trình trợ giúp khác gọi là' dfmt' gọi 'showFFloat' từ Numeric. Những gì nó thực hiện từ đó phụ thuộc vào việc thực hiện, và khi tôi trả lời câu hỏi này, tôi nghĩ rằng tôi đã truy tìm mã GHC với mô-đun 'Data.Text', bây giờ tôi cần phải kiểm tra lại. –

+0

có vẻ như bạn đã đúng: như của GHC 7.4.1 chức năng 'roundTo' được gọi bởi' formatRealFloat' được bao gồm trong tệp đó chứ không phải là một cuộc gọi đến 'Data.Text.v.v. Nó làm điều tương tự, nhưng là nhận thức cơ bản –

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