Hatena::ブログ(Diary)

sirocco の書いてもすぐに忘れるメモ

2011-01-21

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

| 07:47 |

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 には 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
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

happynowhappynow 2012/07/05 03:43 > 少数の型が違う場合は
> 少数は必ず有理数になれる

typo です。少数 → 小数

siroccosirocco 2012/07/05 04:35 id:happynow ありがとうございます。修正しました。

トラックバック - http://d.hatena.ne.jp/sirocco/20110121/1295563675