Россия, Петерубрг, СПБ-ГПУ, 1998 |
Числовые функции
- Базируется на "Быстрой и точной печати чисел с плавающей точкой" - Р.Г. Бургера и Р.К. Дайбвига - ("Printing Floating-Point Numbers Quickly and Accurately" - R.G. Burger и R. K. Dybvig) - в PLDI 96. - Версия, приведенная здесь, использует намного более медленную оценку алгоритма. - Ее следует усовершенствовать. - Эта функция возвращает непустой список цифр (целые числа в диапазоне [0..base-1]) - и экспоненту. В общем случае, если - floatToDigits r = ([a, b, ... z], e) - то - r = 0.ab..z * base^e -
floatToDigits :: (RealFloat a) => Integer -> a -> ([Int], Int) floatToDigits _ 0 = ([], 0) floatToDigits base x = let (f0, e0) = decodeFloat x (minExp0, _) = floatRange x p = floatDigits x b = floatRadix x minExp = minExp0 - p - действительная минимальная экспонента
- В Haskell требуется, чтобы f было скорректировано так, чтобы денормализационные числа - имели невозможно низкую экспоненту. Для этого используется коррекция.
f :: Integer e :: Int (f, e) = let n = minExp - e0 in if n > 0 then (f0 `div` (b^n), e0+n) else (f0, e0) (r, s, mUp, mDn) = if e >= 0 then let be = b^e in if f == b^(p-1) then (f*be*b*2, 2*b, be*b, b) else (f*be*2, 2, be, be) else if e > minExp && f == b^(p-1) then (f*b*2, b^(-e+1)*2, b, 1) else (f*2, b^(-e)*2, 1, 1) k = let k0 = if b==2 && base==10 then - logBase 10 2 немного больше, чем 3/10, поэтому - следующее вызовет ошибку на нижней стороне. Игнорирование - дроби создаст эту ошибку даже больше. - Haskell обещает, что p-1 <= logBase b f < p. (p - 1 + e0) * 3 `div` 10 else ceiling ((log (fromInteger (f+1)) + fromIntegral e * log (fromInteger b)) / log (fromInteger base)) fixup n = if n >= 0 then if r + mUp <= expt base n * s then n else fixup (n+1) else if expt base (-n) * (r + mUp) <= s then n else fixup (n+1) in fixup k0 gen ds rn sN mUpN mDnN = let (dn, rn') = (rn * base) `divMod` sN mUpN' = mUpN * base mDnN' = mDnN * base in case (rn' < mDnN', rn' + mUpN' > sN) of (True, False) -> dn : ds (False, True) -> dn+1 : ds (True, True) -> if rn' * 2 < sN then dn : ds else dn+1 : ds (False, False) -> gen (dn:ds) rn' sN mUpN' mDnN' rds = if k >= 0 then gen [] r (s * expt base k) mUp mDn else let bk = expt base (-k) in gen [] (r * bk) s (mUp * bk) (mDn * bk) in (map fromIntegral (reverse rds), k)
- Эта функция для считывания чисел с плавающей точкой использует менее ограничивающий синтаксис - для чисел с плавающей точкой, чем лексический анализатор Haskell. `.' является необязательной.
readFloat :: (RealFrac a) => ReadS a readFloat r = [(fromRational ((n%1)*10^^(k-d)),t) | (n,d,s) <- readFix r, (k,t) <- readExp s] ++ [ (0/0, t) | ("NaN",t) <- lex r] ++ [ (1/0, t) | ("Infinity",t) <- lex r] where readFix r = [(read (ds++ds'), length ds', t) | (ds,d) <- lexDigits r, (ds',t) <- lexFrac d ] lexFrac ('.':ds) = lexDigits ds lexFrac s = [("",s)] readExp (e:s) | e `elem` "eE" = readExp' s readExp s = [(0,s)] readExp' ('-':s) = [(-k,t) | (k,t) <- readDec s] readExp' ('+':s) = readDec s readExp' s = readDec s lexDigits :: ReadS String lexDigits = nonnull isDigit nonnull :: (Char -> Bool) -> ReadS String nonnull p s = [(cs,t) | (cs@(_:_),t) <- [span p s]]