Округление до определенного количества цифр в Haskell

Я пытаюсь сделать функцию для округления числа с плавающей точкой до определенной длины цифр. До сих пор я пришел к следующему выводу:--11-->

import Numeric;

digs :: Integral x => x -> [x] <br>
digs 0 = [] <br>
digs x = digs (x `div` 10) ++ [x `mod` 10]

roundTo x t = let d = length $ digs $ round x <br>
                  roundToMachine x t = (fromInteger $ round $ x * 10^^t) * 10^^(-t)
              in roundToMachine x (t - d)

я использую digs функция для определения количества цифр перед запятой для оптимизации входного значения (т. е. переместить все мимо запятой, так что 1.234 становится 0.1234 * 10^1)

на roundTo функция, кажется, работает для большинства входных данных, однако для некоторых входов я получаю странные результаты, например roundTo 1.0014 4 производит 1.0010000000000001 вместо 1.001.

проблема в этом примере вызвана вычислением 1001 * 1.0e-3 (который возвращает 1.0010000000000001)

это просто проблема в числовом представлении Haskell, с которым я должен жить, или есть лучший способ округлить число с плавающей запятой до определенной длины цифр?

2 ответов


это не проблема haskell, а проблема с плавающей запятой. Поскольку каждое число с плавающей запятой реализовано в конечном числе битов, существуют числа, которые не могут быть представлены полностью точно. Вы также можете увидеть это, вычисляя 0.1 + 0.2, который неуклюже возвращает 0.30000000000000004 вместо 0.3. Это связано с тем, как числа с плавающей запятой реализуются для вашего языка и аппаратной архитектуры.

решение продолжить использование roundTo функция для выполнения вычислений (это так же точно, как вы получите без специальных библиотек), но если вы хотите распечатать его на экране, вы должны использовать форматирование строк, такое как


Я понимаю, что этот вопрос был опубликован почти 2 года назад, но я думал, что у меня будет ответ, который не требует преобразования строк.

-- x : number you want rounded, n : number of decimal places you want...
truncate' :: Double -> Int -> Double
truncate' x n = (fromIntegral (floor (x * t))) / t
    where t = 10^n

-- How to answer your problem...
λ truncate' 1.0014 3
1.001

-- 2 digits of a recurring decimal please...
λ truncate' (1/3) 2
0.33

-- How about 6 digits of pi?
λ truncate' pi 6
3.141592

Я не проверял его тщательно, поэтому, если вы найдете номера, это не работает, дайте мне знать!