Haskell - конвертировать целое число в двойное, затем в строку - PullRequest
0 голосов
/ 11 октября 2018

Я практикую Haskell, написав программу, которая преобразует цену в центах в доллары.Очевидно, это означает, что мой Int должен стать Double, но я хочу, чтобы мой конечный результат был String.Мой код:

Сначала я определил свой тип Price:

type Price = Int

Затем я написал свой модуль:

formatDollars :: Price -> String
formatDollars x = show (fromIntegral (x `div` 100))

Мой тестовый ввод для x был 188, что должно дать мне 1,88.

Но мой вывод всегда "1".Это заставляет меня думать, что моя функция fromIntegral может неправильно использоваться в моем коде, как будто она усекается после десятичной дроби.Поскольку я так новичок в Хаскеле, я уверен, что это простая ошибка;кто-нибудь может мне помочь?

Ответы [ 2 ]

0 голосов
/ 11 октября 2018
188 `div` 100 == 1

Вы хотите преобразовать в Double до деления.(А затем делайте настоящее деление, а не целочисленное деление.)

formatDollars x = show ((fromIntegral x) / 100)
0 голосов
/ 11 октября 2018

div - это целое число деление.Таким образом, это означает, что div 188 100, вы получите:

Prelude> div 188 100
1

Так что это означает, что после div, 88 исчезает.Кроме того div усекает до отрицательной бесконечности.В результате, если число отрицательное, мы получаем:

Prelude> div (-210) 100
-3

Но вам, вероятно, лучше не преобразовывать число в Float в любом случае: Float s не способны точно представлять десятичные числа: приближенияиспользуются.Например, если вы напишите 0.82, будет сохранено значение, которое выглядит как 0.8200000000000001 (хотя это опять-таки не точное представление).Если число достаточно велико, мантисса не всегда способна представить все число, поэтому при преобразовании может возникнуть ошибка.Наконец, если вы печатаете маленькие или большие числа, по умолчанию show будет использовать научную нотацию (это, конечно, не «изначально» проблема, но я думаю, что это довольно странно, если вы печатаете цену, я никогда не видел супермаркетс метками вроде $ 9e-2):

Prelude> 0.01 :: Float
1.0e-2

Что, вероятно, не то, что вы хотите.

Вы можете просто разделить число на три части: значение до десятичной точки идве цифры после десятичной точки.Например:

import Data.Char(intToDigit)

formatDollars :: Price -> String
formatDollars pr = show pr0 ++ ['.', pr1, pr2]
    where pr0 = quot pr 100
          digit n = intToDigit (abs (rem n 10))
          pr1 = digit (quot pr 10)
          pr2 = digit pr

Пакет Decimal [hackage] также может быть вариантом.DecimalRaw представляется таким образом, что для десятичной системы 1035 * он может точно представлять числа.Например:

Prelude> import Data.Decimal
Prelude Data.Decimal> Decimal 2 188
1.88 

Таким образом, мы можем избежать всех неприятностей и написать это как:

import Data.Decimal(DecimalRaw(Decimal))

formatDollars :: Price -> String
formatDollars = show . Decimal 2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...