2013-04-23 21 views
6

Mam problem dotyczący Haskell floor funkcję - to ma powrotu „największą liczbę całkowitą nie większą niż argument”, ale wyrazemHaskell funkcja piętro zwraca różne wyniki

floor 3.9999999999999999

zwraca 4 zamiast 3. Może to mieć coś wspólnego z precyzją typu Double, ale potem nie powinno się go kompilować, biorąc pod uwagę znaczenie bezpieczeństwa typu Haskella, w każdym razie w tym przypadku zwraca liczbę większą niż argument sprzeczny z jej definicją.

+11

Bez względu na to, ile bitów Twój zmiennoprzecinkowa ma format liczbowy, jest skończony i zaokrąglić stałych nie może dokładnie reprezentują. Wydaje się, że "3,9999999999999999" jest zaokrąglone do "4.0", gdy jest konwertowane na podwójne, zanim jeszcze "floor" to zobaczy. – tauli

Odpowiedz

22

w tym przypadku zwraca liczbę większą niż argument sprzeczny z definicją.

Zwraca liczbę równą jej argumentowi. Jak powiedziałeś, chodzi o podwójną precyzję. Liczby 3.9999999999999999 i 4 są po prostu równe sobie w ramach 64-bitowych reguł zmiennoprzecinkowych.

ale to nie powinien skompilować podane znaczenie typu Haskell bezpieczeństwa

Problem polega na tym, że jak literały ułamkowe mają polimorficzne typu Fractional a => a. To nie muszą być duble. Na przykład możesz napisać floor (3.9999999999999999 :: Rational), który poprawnie zwróci 3, ponieważ 3.9999999999999999 może być reprezentowany jako Rational bez utraty dokładności.

Jeśli Haskell popełnił błąd podczas pisania 3.9999999999999999, to również nie można zapisać 3.9999999999999999 :: Rational, co byłoby złe. Ponieważ literał Fractional może być reprezentowany przy użyciu wielu różnych typów, z których niektóre mają nieskończoną dokładność, byłoby dużym błędem dla Haskella ograniczenie liczby legalnych Fractional literałów w oparciu o ograniczenia Double.

Można argumentować, że Haskell powinien ograniczyć 3.9999999999999999, gdy jest używany jako Double, ale nie, gdy jest używany Rational. Wymagałoby to jednak wystąpienia klasy typu Fractional w celu zadeklarowania informacji na temat ich dokładności (aby Haskell mógł wykorzystać te informacje do podjęcia decyzji, czy dany tekst jest ważny dla tego typu), którego obecnie nie ma i które byłoby trudne (lub niemożliwe).) w celu wdrożenia w sposób ogólny, skuteczny i przyjazny dla użytkownika (biorąc pod uwagę, że termin "precyzja" może oznaczać całkiem różne rzeczy w zależności od tego, czy mówimy o liczbach zmiennoprzecinkowych, czy o stałych numerach punktów i czy używają bazy 2 lub 10 (lub cokolwiek innego) do reprezentowania liczb - każda z nich byłaby możliwa dla wystąpień klasy typu Fractional).

9

Nie dotyczy to bezpieczeństwa typu. Sprawdź na przykład wartość na http://babbage.cs.qc.cuny.edu/IEEE-754/. Wartości 3.9999999999999999 i 4 są dokładnie takie same dla liczb zmiennoprzecinkowych, jeśli długość jest krótsza lub równa 64 bitom. Wartość, którą otrzymujesz, nie jest większa - jest dokładnie taka sama.

Jeśli potrzebujesz tak wysokiej precyzji, rzucić okiem na http://www.haskell.org/haskellwiki/Libraries_and_tools/Mathematics#Arbitrary_precision

+0

Myślę, że chodziło o to, że jeśli użytkownik spróbuje napisać "Double", który nie może być właściwie przedstawiony jako "Double", a kompilator na to pozwala, to jest to rodzaj błędu typu, którego kompilator nie złapał. – sepp2k

+2

@ sepp2k: jeśli chcesz, aby był to błąd kompilacji, powinieneś wiedzieć, jakie będą tego konsekwencje. "0,1", na przykład, nie może być dokładnie reprezentowane przez Double (lub dowolny binarny format zmiennoprzecinkowy iirc). Ale zrobienie '(0.1 :: Double)' błędu kompilacji byłoby niepraktyczne. – tauli

+0

@tauli Nie chcę niczego, tłumaczyłem (moja interpretacja) punkt PO. Myślę również, że w tym kontekście możemy zdefiniować "literał, który może dokładnie odwzorować jako Double", aby oznaczać "liczbę, której reprezentacja łańcuchowa będzie równoznaczna z literałem", co doprowadziłoby do pewnych użytecznych reguł dotyczących tego, które literały będą akceptowane a które nie. – sepp2k

Powiązane problemy