Haskell における数値間の変換(Int・Integer・Float・Double・Rational )

Haskell は数値の型を合わせないと計算出来ません。それは他の言語も同じですが、 Haskell には型推論があるため、変換しようとする型を直接指定するのでなく、中間としてジェネリックな型を指定して、あとは Haskell におまかせするというようなイメージです。
(参考:Real World Haskell p153 表6-4 数値間の変換)
また、コンパイラが勝手にキャストとするということはありません。
整数(Int、Integer)の型が違う場合は fromIntegral を使って Num にして型を合わせてから計算する。

fromIntegral は Integral型からNum型に変換する関数です。

ghci> (fromIntegral (2::Int)) + 3::Integer  --> 5

ghci> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b
  • Num 型は必要に応じてインスタンスが選択されます。

Num には Integer、Int、Float、Double のインスタンスがありますので、Integerのインスタンスを使って(+)の計算が行われます。

ghci> :i Num
class Num a where
  (+) :: a -> a -> a
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
  	-- Defined in `GHC.Num'
instance Num Integer -- Defined in `GHC.Num'
instance Num Int -- Defined in `GHC.Num'
instance Num Float -- Defined in `GHC.Float'
instance Num Double -- Defined in `GHC.Float'

ghci> :t (fromIntegral (2::Int))::Double --> (fromIntegral (2::Int))::Double :: Double
ghci> :t (fromIntegral (2::Int))::Float  --> (fromIntegral (2::Int))::Float :: Float
ghci> :t (fromIntegral (2::Int))::Integer--> (fromIntegral (2::Int))::Integer :: Integer

ghci> (fromIntegral (2::Int)) * (2::Integer)     -- > 4
ghci> (fromIntegral (2::Int)) * (2::Float)       -- > 4.0
ghci> (fromIntegral (2::Int)) * (2::Double)      -- > 4.0

Integralクラスには IntegerとIntのインスタンスがある。

class (Real a, Enum a) => Integral a where
  quot :: a -> a -> a
  rem :: a -> a -> a
  div :: a -> a -> a
  mod :: a -> a -> a
  quotRem :: a -> a -> (a, a)
  divMod :: a -> a -> (a, a)
  toInteger :: a -> Integer
  	-- Defined in GHC.Real
instance Integral Integer -- Defined in GHC.Real
instance Integral Int -- Defined in GHC.Real

小数の型が違う場合は realToFrac を使って Real(Int、Integer、Float、Double) から Fractionalへ変換してから計算を行う。

> (realToFrac (2::Int)) / 3.2            --> 0.625
> (realToFrac (2::Int)) / (3.2::Double)  --> 0.625
> (realToFrac (2::Int)) / (3.2::Float)   --> 0.625

> :t realToFrac 
realToFrac :: (Real a, Fractional b) => a -> b

-- Fractional型にはFloat、Double のインスタンスがありますので、必要に応じてFloat、Doubleの計算が行われます。
> :t (realToFrac (2.3::Double))::Float  --> (realToFrac (2.3::Double))::Float :: Float
> :t (realToFrac (2.3::Double))::Double --> (realToFrac (2.3::Double))::Double :: Double

-- realToFrac は Real(Int、Integer、Float、Double)をFractional(Float、Double)
-- に変換できる。
Prelude> :i Real
class (Num a, Ord a) => Real a where toRational :: a -> Rational
  	-- Defined in GHC.Real
instance Real Integer -- Defined in GHC.Real
instance Real Int -- Defined in GHC.Real
instance Real Float -- Defined in GHC.Float
instance Real Double -- Defined in GHC.Float

> :t (realToFrac (2::Int))::Double -->(realToFrac (2::Int))::Double :: Double
> (realToFrac (2::Int))::Double     -->2.0
> (realToFrac (2::Integer))::Double -->2.0
> (realToFrac (2::Float))::Double   -->2.0
  • 整数とも浮動小数点とも解釈できるものは、 処理系が文脈に沿って解釈してくれます。

紫藤のページ / Haskell のお勉強 / 4. 型 / 1.3. 数値

ghci>  2 / 3     --> 0.6666666666666666
-- (/) は Fractional 型の引数を二つとりFractional 型を返す関数なので 2,3のリテラルは
-- Fractional 型と 解釈されている。
-- (/) :: (Fractional a) => a -> a -> a
ghci> :t 2 / 3   --> 2 / 3 :: (Fractional t) => t
  • 整数(Int,Integer)には(/)instanceがないので割り算はエラーになる。
ghci>  (2 :: Int) / (3 :: Int)  
<interactive>:1:0:
    No instance for (Fractional Int)
      arising from a use of `/' at <interactive>:1:0-22
    Possible fix: add an instance declaration for (Fractional Int)
    In the expression: (2 :: Int) / (3 :: Int)
    In the definition of `it': it = (2 :: Int) / (3 :: Int)

-- Num型にすれば OK
ghci> (fromIntegral (2 :: Int)) / (fromIntegral (3 :: Int))  --> 0.6666666666666666
  • Rational は有理数(分数で表せる数字)
ghci> toRational  (1.2::Double)  --> 5404319552844595 % 4503599627370496
ghci> toRational  (0.25::Double) --> 1 % 4
ghci> toRational  (0::Double)    --> 0 % 1
ghci> toRational  (1000::Double) --> 1000 % 1

-- Ratio モジュールをインポート
ghci> :m + Ratio 
ghci> :i %
(%) :: (Integral a) => a -> a -> Ratio a 	-- Defined in GHC.Real
infixl 7 %
  • 有理数を作ってみる。
> 1 % 2                   -- > 1 % 2
> fromRational $ 1 % 2    -- > 0.5
> 1 / 3 ::Rational        -- > 1 % 3

-- fromRational で出来るのは Fractional という型。
ghci> :t fromRational $ 1 % 2        --> fromRational $ 1 % 2 :: (Fractional a) => a
ghci> 1 % 3                          --> 1 % 3
ghci> fromRational $ 1 % 3           --> 0.3333333333333333
ghci> (fromRational $ 1 % 3)::Double --> 0.3333333333333333
ghci> (fromRational $ 1 % 3)::Float  --> 0.33333334
  • Double から Flot への変換。小数は必ず有理数になれる。Double も Flot も分数に変換し、Flot、Doubleに変換すればよい。
ghci> toRational  (1.2::Double)                         --> 5404319552844595 % 4503599627370496
ghci> (fromRational $ toRational  (1.2::Double))::Float -- > 1.2
  • Fractional というのは Double、Float になれる数。
class (Num a) => Fractional a where
  (/) :: a -> a -> a
  recip :: a -> a
  fromRational :: Rational -> a
  	-- Defined in GHC.Real
instance Fractional Float -- Defined in GHC.Float
instance Fractional Double -- Defined in GHC.Float

ghci> :t (fromRational $ toRational  (1.2::Double))
  --> (fromRational $ toRational  (1.2::Double)) :: (Fractional a) => a

ghci> (fromRational $ toRational  (1.2::Double)) + 1.2::Float   --> 2.4
ghci> (fromRational $ toRational  (1.2::Double)) + 1.2::Double  --> 2.4
> :m + GHC.Float
> float2Double (1::Float)          --> 1.0
> :t float2Double (1::Float)       -- > float2Double (1::Float) :: Double

> :t double2Float (1::Double)      -- > double2Float (1::Double) :: Float
> (1.234567890123456789::Double)   -- > 1.2345678901234567
> double2Float (1.234567890123456789::Double) -- > 1.2345679
  • Double、Float から Int, Integer へは小数点以下を切り捨てる。(Cookbook/Numbers)
ghci> :t (truncate  (1.2::Double))
(truncate  (1.2::Double)) :: (Integral b) => b

ghci> :t (truncate  (1.2::Double))::Int
(truncate  (1.2::Double))::Int :: Int