packages feed

decimal-arithmetic 0.1.0.1 → 0.2.0.0

raw patch · 17 files changed

+2187/−686 lines, 17 filesdep +doctestdep +mtlPVP ok

version bump matches the API change (PVP)

Dependencies added: doctest, mtl

API changes (from Hackage documentation)

- Numeric.Decimal: data Number p r
- Numeric.Decimal: data PPlus1 p
- Numeric.Decimal: data PTimes2 p
+ Numeric.Decimal: data Decimal p r
+ Numeric.Decimal: toBool :: Decimal p r -> Bool
+ Numeric.Decimal: type PPlus1 p = PPlus p P1 A precision of (@p@ + 1) significant digits
+ Numeric.Decimal: type PPlus2 p = PPlus p P2 A precision of (@p@ + 2) significant digits
+ Numeric.Decimal: type PPlus3 p = PPlus p P3 A precision of (@p@ + 3) significant digits
+ Numeric.Decimal: type PPlus4 p = PPlus p P4 A precision of (@p@ + 4) significant digits
+ Numeric.Decimal: type PPlus5 p = PPlus p P5 A precision of (@p@ + 5) significant digits
+ Numeric.Decimal: type PPlus6 p = PPlus p P6 A precision of (@p@ + 6) significant digits
+ Numeric.Decimal: type PPlus7 p = PPlus p P7 A precision of (@p@ + 7) significant digits
+ Numeric.Decimal: type PPlus8 p = PPlus p P8 A precision of (@p@ + 8) significant digits
+ Numeric.Decimal: type PPlus9 p = PPlus p P9 A precision of (@p@ + 9) significant digits
+ Numeric.Decimal: type PTimes10 = PTimes P10
+ Numeric.Decimal: type PTimes2 = PTimes P2
+ Numeric.Decimal.Arithmetic: Clamped :: Signal
+ Numeric.Decimal.Arithmetic: DivisionByZero :: Signal
+ Numeric.Decimal.Arithmetic: Inexact :: Signal
+ Numeric.Decimal.Arithmetic: InvalidOperation :: Signal
+ Numeric.Decimal.Arithmetic: Overflow :: Signal
+ Numeric.Decimal.Arithmetic: Rounded :: Signal
+ Numeric.Decimal.Arithmetic: Subnormal :: Signal
+ Numeric.Decimal.Arithmetic: Underflow :: Signal
+ Numeric.Decimal.Arithmetic: basicDefaultContext :: Context P9 RoundHalfUp
+ Numeric.Decimal.Arithmetic: clearFlags :: Signals -> Arith p r ()
+ Numeric.Decimal.Arithmetic: data Arith p r a
+ Numeric.Decimal.Arithmetic: data Context p r
+ Numeric.Decimal.Arithmetic: data Exception p r
+ Numeric.Decimal.Arithmetic: data Signal
+ Numeric.Decimal.Arithmetic: data Signals
+ Numeric.Decimal.Arithmetic: evalArith :: Arith p r a -> Context p r -> Either (Exception p r) a
+ Numeric.Decimal.Arithmetic: exceptionResult :: Exception p r -> Decimal p r
+ Numeric.Decimal.Arithmetic: exceptionSignal :: Exception p r -> Signal
+ Numeric.Decimal.Arithmetic: extendedDefaultContext :: Context p RoundHalfEven
+ Numeric.Decimal.Arithmetic: flags :: Context p r -> Signals
+ Numeric.Decimal.Arithmetic: instance Control.Monad.Error.Class.MonadError (Numeric.Decimal.Arithmetic.Exception p r) (Numeric.Decimal.Arithmetic.Arith p r)
+ Numeric.Decimal.Arithmetic: instance Control.Monad.State.Class.MonadState (Numeric.Decimal.Arithmetic.Context p r) (Numeric.Decimal.Arithmetic.Arith p r)
+ Numeric.Decimal.Arithmetic: instance GHC.Base.Applicative (Numeric.Decimal.Arithmetic.Arith p r)
+ Numeric.Decimal.Arithmetic: instance GHC.Base.Functor (Numeric.Decimal.Arithmetic.Arith p r)
+ Numeric.Decimal.Arithmetic: instance GHC.Base.Monad (Numeric.Decimal.Arithmetic.Arith p r)
+ Numeric.Decimal.Arithmetic: instance GHC.Base.Monoid Numeric.Decimal.Arithmetic.Signals
+ Numeric.Decimal.Arithmetic: instance GHC.Classes.Eq Numeric.Decimal.Arithmetic.Signal
+ Numeric.Decimal.Arithmetic: instance GHC.Classes.Eq Numeric.Decimal.Arithmetic.Signals
+ Numeric.Decimal.Arithmetic: instance GHC.Enum.Bounded Numeric.Decimal.Arithmetic.Signal
+ Numeric.Decimal.Arithmetic: instance GHC.Enum.Enum Numeric.Decimal.Arithmetic.Signal
+ Numeric.Decimal.Arithmetic: instance GHC.Show.Show (Numeric.Decimal.Arithmetic.Exception p r)
+ Numeric.Decimal.Arithmetic: instance GHC.Show.Show Numeric.Decimal.Arithmetic.Signal
+ Numeric.Decimal.Arithmetic: instance GHC.Show.Show Numeric.Decimal.Arithmetic.Signals
+ Numeric.Decimal.Arithmetic: newContext :: Context p r
+ Numeric.Decimal.Arithmetic: raiseSignal :: Signal -> Decimal p r -> Arith p r (Decimal p r)
+ Numeric.Decimal.Arithmetic: runArith :: Arith p r a -> Context p r -> (Either (Exception p r) a, Context p r)
+ Numeric.Decimal.Arithmetic: signal :: Signal -> Signals
+ Numeric.Decimal.Arithmetic: signalMember :: Signal -> Signals -> Bool
+ Numeric.Decimal.Arithmetic: signals :: [Signal] -> Signals
+ Numeric.Decimal.Arithmetic: trap :: Signals -> TrapHandler p r -> Arith p r a -> Arith p r a
+ Numeric.Decimal.Arithmetic: type TrapHandler p r = Exception p r -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: Class :: Sign -> Subclass -> Class
+ Numeric.Decimal.Operation: InfinityClass :: Subclass
+ Numeric.Decimal.Operation: NaNClass :: Subclass
+ Numeric.Decimal.Operation: Neg :: Sign
+ Numeric.Decimal.Operation: NormalClass :: Subclass
+ Numeric.Decimal.Operation: Pos :: Sign
+ Numeric.Decimal.Operation: SubnormalClass :: Subclass
+ Numeric.Decimal.Operation: ZeroClass :: Subclass
+ Numeric.Decimal.Operation: canonical :: Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: class_ :: Precision a => Decimal a b -> Arith p r Class
+ Numeric.Decimal.Operation: copy :: Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: copyAbs :: Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: copyNegate :: Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: copySign :: Decimal a b -> Decimal c d -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: data Class
+ Numeric.Decimal.Operation: data Sign
+ Numeric.Decimal.Operation: data Subclass
+ Numeric.Decimal.Operation: instance GHC.Classes.Eq Numeric.Decimal.Operation.Class
+ Numeric.Decimal.Operation: instance GHC.Classes.Eq Numeric.Decimal.Operation.Subclass
+ Numeric.Decimal.Operation: instance GHC.Show.Show Numeric.Decimal.Operation.Class
+ Numeric.Decimal.Operation: isCanonical :: Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: isFinite :: Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: isInfinite :: Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: isNaN :: Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: isNormal :: Precision a => Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: isQNaN :: Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: isSNaN :: Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: isSigned :: Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: isSubnormal :: Precision a => Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: isZero :: Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: max :: (Precision p, Rounding r) => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: maxMagnitude :: (Precision p, Rounding r) => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: min :: (Precision p, Rounding r) => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: minMagnitude :: (Precision p, Rounding r) => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
+ Numeric.Decimal.Operation: radix :: Precision p => Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: reduce :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)
+ Numeric.Decimal.Operation: sameQuantum :: Decimal a b -> Decimal c d -> Arith p r (Decimal p r)
- Numeric.Decimal: cast :: (Precision p, Rounding r) => Number a b -> Number p r
+ Numeric.Decimal: cast :: (Precision p, Rounding r) => Decimal a b -> Decimal p r
- Numeric.Decimal: class Rounding r where isRoundFloor _ = False
+ Numeric.Decimal: class Rounding r
- Numeric.Decimal: type BasicDecimal = Number P9 RoundHalfUp
+ Numeric.Decimal: type BasicDecimal = Decimal P9 RoundHalfUp
- Numeric.Decimal: type ExtendedDecimal p = Number p RoundHalfEven
+ Numeric.Decimal: type ExtendedDecimal p = Decimal p RoundHalfEven
- Numeric.Decimal: type P2 = PTimes2 P1
+ Numeric.Decimal: type P2 = PPlus1 P1
- Numeric.Decimal: type P250 = PTimes2 P125
+ Numeric.Decimal: type P250 = PTimes10 P25
- Numeric.Decimal: type P75 = PPlus1 P74
+ Numeric.Decimal: type P75 = PPlus5 (PTimes10 P7)
- Numeric.Decimal.Conversion: toEngineeringString :: Number p r -> ShowS
+ Numeric.Decimal.Conversion: toEngineeringString :: Decimal p r -> ShowS
- Numeric.Decimal.Conversion: toNumber :: (Precision p, Rounding r) => ReadP (Number p r)
+ Numeric.Decimal.Conversion: toNumber :: ReadP (Decimal PInfinite r)
- Numeric.Decimal.Conversion: toScientificString :: Number p r -> ShowS
+ Numeric.Decimal.Conversion: toScientificString :: Decimal p r -> ShowS
- Numeric.Decimal.Operation: abs :: (Precision p, Rounding r) => Number p r -> Number p r
+ Numeric.Decimal.Operation: abs :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)
- Numeric.Decimal.Operation: add :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r
+ Numeric.Decimal.Operation: add :: (Precision p, Rounding r) => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)
- Numeric.Decimal.Operation: compare :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r
+ Numeric.Decimal.Operation: compare :: (Precision p, Rounding r) => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)
- Numeric.Decimal.Operation: divide :: (FinitePrecision p, Rounding r) => Number p r -> Number p r -> Number p r
+ Numeric.Decimal.Operation: divide :: (FinitePrecision p, Rounding r) => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)
- Numeric.Decimal.Operation: minus :: (Precision p, Rounding r) => Number p r -> Number p r
+ Numeric.Decimal.Operation: minus :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)
- Numeric.Decimal.Operation: multiply :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r
+ Numeric.Decimal.Operation: multiply :: (Precision p, Rounding r) => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)
- Numeric.Decimal.Operation: plus :: (Precision p, Rounding r) => Number p r -> Number p r
+ Numeric.Decimal.Operation: plus :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)
- Numeric.Decimal.Operation: subtract :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r
+ Numeric.Decimal.Operation: subtract :: (Precision p, Rounding r) => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)

Files

README.md view
@@ -11,4 +11,4 @@   [General Decimal Arithmetic]: http://speleotrove.com/decimal/  While usable, the implementation is currently in its infancy. Additional-operations as well as an API for manipulating context flags are planned.+operations and possible API changes are planned.
TODO view
@@ -1,7 +1,5 @@ -*- Outline -*-  * To Do-** Test suite-** instance Floating (Number p r)-** instance RealFloat (Number p r)-** instance PrintfArg (Number p r)+** instance Floating (Decimal p r)+** instance PrintfArg (Decimal p r)
decimal-arithmetic.cabal view
@@ -1,6 +1,6 @@  name:                decimal-arithmetic-version:             0.1.0.1+version:             0.2.0.0  synopsis:            An implementation of Mike Cowlishaw's                      General Decimal Arithmetic Specification@@ -39,22 +39,27 @@    exposed-modules:     Numeric.Decimal                        Numeric.Decimal.Conversion+                       Numeric.Decimal.Arithmetic                        Numeric.Decimal.Operation   other-modules:       Numeric.Decimal.Number                        Numeric.Decimal.Precision                        Numeric.Decimal.Rounding    build-depends:       base >= 4.7 && < 5+                     , mtl   default-language:    Haskell2010   default-extensions:  Trustworthy-  other-extensions:    RoleAnnotations+  other-extensions:    FlexibleInstances+                       MultiParamTypeClasses+                       RoleAnnotations -test-suite decimal-arithmetic-test+test-suite doctests   type:                exitcode-stdio-1.0   hs-source-dirs:      test-  main-is:             Spec.hs+  main-is:             doctests.hs   build-depends:       base                      , decimal-arithmetic+                     , doctest >= 0.8                      , QuickCheck   ghc-options:         -threaded -rtsopts -with-rtsopts=-N   default-language:    Haskell2010
src/Numeric/Decimal.hs view
@@ -6,7 +6,7 @@ Maintainer  : rob@mars.org Stability   : experimental -This module provides a general-purpose 'Number' type supporting decimal+This module provides a general-purpose number type supporting decimal arithmetic for both limited precision floating-point (IEEE 754-2008) and for arbitrary precision floating-point (following the same principles as IEEE 754 and IEEE 854-1987) as described in the@@ -14,13 +14,28 @@ by Mike Cowlishaw. In addition to floating-point arithmetic, integer and unrounded floating-point arithmetic are included as subsets. -Unlike the binary floating-point types 'Float' and 'Double', the 'Number' type-can represent and perform arithmetic with decimal numbers exactly.-Internally, a 'Number' is represented with an integral coefficient and base-10-exponent.+Unlike the binary floating-point types 'Float' and 'Double', decimal number+types can perform decimal arithmetic exactly. Internally, decimal numbers are+represented with an integral coefficient and base-10 exponent. -The 'Number' type supports lossless conversion to and from a string-representation via the 'Show' and 'Read' instances. Note that there may be+@+@>>>@ __29.99 + 4.71 :: Double__+34.699999999999996+@++>>> 29.99 + 4.71 :: BasicDecimal+34.70++@+@>>>@ __0.1 + 0.2 == (0.3 :: Double)__+False+@++>>> 0.1 + 0.2 == (0.3 :: BasicDecimal)+True++Decimal numbers support lossless conversion to and from a string+representation via 'Show' and 'Read' instances. Note that there may be multiple representations of values that are numerically equal (e.g. 1 and 1.00) which are preserved by this conversion. -}@@ -29,7 +44,7 @@          -- $usage           -- * Arbitrary-precision decimal numbers-         Number+         Decimal        , BasicDecimal        , ExtendedDecimal        , GeneralDecimal@@ -51,61 +66,61 @@           -- * Functions        , cast+       , toBool        ) where  import Numeric.Decimal.Number import Numeric.Decimal.Precision import Numeric.Decimal.Rounding --- | A basic decimal floating point number with 9 digits of precision, rounding half up-type BasicDecimal = Number P9 RoundHalfUp+-- | A decimal floating point number with 9 digits of precision, rounding half+-- up+type BasicDecimal = Decimal P9 RoundHalfUp --- | A decimal floating point number with selectable precision, rounding half even-type ExtendedDecimal p = Number p  RoundHalfEven+-- | A decimal floating point number with selectable precision, rounding half+-- even+type ExtendedDecimal p = Decimal p RoundHalfEven  -- | A decimal floating point number with infinite precision type GeneralDecimal = ExtendedDecimal PInfinite -basicDefaultContext :: TrapHandler P9 RoundHalfUp -> Context P9 RoundHalfUp-basicDefaultContext handler = defaultContext { trapHandler = trap }-  where trap Inexact   = id-        trap Rounded   = id-        trap Subnormal = id-        trap sig       = handler sig+{- $usage -extendedDefaultContext :: Context p RoundHalfEven-extendedDefaultContext = defaultContext+You should choose a decimal number type with appropriate precision and+rounding to use in your application. There are several options: --- $usage------ It is recommended to create an alias for the type of numbers you wish to--- support in your application. For example:------ >  type Decimal = BasicDecimal------ This is a basic number type with 9 decimal digits of precision that rounds--- half up.------ >  type Decimal = ExtendedDecimal P15------ This is a number type with 15 decimal digits of precision that rounds half--- even. There are a range of ready-made precisions available, including 'P1'--- through 'P50' on up to 'P2000'. Alternatively, an arbitrary precision can--- be constructed through type application of 'PPlus1' and/or 'PTimes2' to any--- existing precision.------ >  type Decimal = GeneralDecimal------ This is a number type with infinite precision. Note that not all operations--- support numbers with infinite precision.------ >  type Decimal = Number P34 RoundDown------ This is a custom number type with 34 decimal digits of precision that--- rounds down (truncates). Several 'Rounding' algorithms are available to--- choose from.------ A decimal number type may be used in a @default@ declaration, possibly--- replacing 'Double' or 'Integer'. For example:------ >  default (Integer, Decimal)+* 'BasicDecimal' is a number type with 9 decimal digits of precision that+rounds half up.++* 'ExtendedDecimal' is a number type constructor with selectable precision+that rounds half even. For example, @'ExtendedDecimal' 'P34'@ is a number type+with 34 decimal digits of precision. There is a range of ready-made precisions+available, including 'P1' through 'P50' on up to 'P2000' (the IEEE 754+smallest and basic formats correspond to precisions 'P7', 'P16', or 'P34').+Alternatively, an arbitrary precision can be constructed through type+application of 'PPlus1' and/or 'PTimes2' to any existing precision.++* 'GeneralDecimal' is a number type with infinite precision. Note that not all+operations support numbers with infinite precision.++* The most versatile 'Decimal' type constructor is parameterized by both a+precision and a rounding algorithm. For example, @'Decimal' 'P20' 'RoundDown'@+is a number type with 20 decimal digits of precision that rounds down+(truncates). Several 'Rounding' algorithms are available to choose from.++It is suggested to create an alias for the type of numbers you wish to support+in your application. For example:++> type Number = ExtendedDecimal P16++A decimal number type may be used in a @default@ declaration, possibly+replacing 'Double' and/or 'Integer'. For example:++> default (Integer, BasicDecimal)++== Advanced usage++Additional operations and control beyond what is provided by the basic numeric+type classes are available through the use of "Numeric.Decimal.Arithmetic" and+"Numeric.Decimal.Operation".+-}
+ src/Numeric/Decimal/Arithmetic.hs view
@@ -0,0 +1,264 @@++{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}++-- | It is not usually necessary to import this module unless you want to use+-- the arithmetic operations from "Numeric.Decimal.Operation" or you need+-- precise control over the handling of exceptional conditions in an+-- arithmetic computation.++module Numeric.Decimal.Arithmetic+       ( -- * Decimal arithmetic+         -- $decimal-arithmetic++         -- ** Context+         Context+       , newContext+       , flags++         -- *** Default contexts+         -- $default-contexts+       , basicDefaultContext+       , extendedDefaultContext++         -- ** The Arith monad+       , Arith+       , runArith+       , evalArith++         -- * Exceptional conditions+         -- $exceptional-conditions+       , Exception+       , exceptionSignal+       , exceptionResult++         -- ** Signals+       , Signal(..)+       , Signals+       , signal+       , signals+       , signalMember++       , raiseSignal+       , clearFlags++         -- ** Traps+       , TrapHandler+       , trap+       ) where++import Control.Monad.Except (MonadError(throwError, catchError),+                             ExceptT, runExceptT)+import Control.Monad.State (MonadState(get, put), modify, gets,+                            State, runState, evalState)+import Data.Bits (bit, complement, testBit, (.&.), (.|.))+import Data.Monoid ((<>))++import Numeric.Decimal.Number+import Numeric.Decimal.Precision+import Numeric.Decimal.Rounding++-- $decimal-arithmetic+--+-- Decimal arithmetic is performed within a context that maintains state to+-- handle exceptional conditions such as underflow, rounding, or division by+-- zero (cf. 'Signal'). The 'Arith' monad provides a means to evaluate an+-- arithmetic computation and manipulate its 'Context'.++-- | A context for decimal arithmetic, carrying signal flags, trap enabler+-- state, and a trap handler, parameterized by precision @p@ and rounding+-- algorithm @r@+data Context p r =+  Context { flags        :: Signals+                            -- ^ The current signal flags of the context+          , trapHandler  :: TrapHandler p r+                            -- ^ The trap handler function for the context+          }++-- | Return a new context with all signal flags cleared and all traps disabled.+newContext :: Context p r+newContext = Context { flags       = mempty+                     , trapHandler = return . exceptionResult+                     }++-- $default-contexts+--+-- The /General Decimal Arithmetic/ specification defines optional default+-- contexts, which define suitable settings for basic arithmetic and for the+-- extended arithmetic defined by IEEE 854 and IEEE 754.++-- | Return a new context with all signal flags cleared, all traps enabled+-- except for 'Inexact', 'Rounded', and 'Subnormal', using a precision of 9+-- significant decimal digits, and rounding half up. Trapped signals simply+-- call 'throwError' with the corresponding 'Exception', and can be caught+-- using 'catchError'.+basicDefaultContext :: Context P9 RoundHalfUp+basicDefaultContext = newContext { trapHandler = handler }+  where handler e+          | exceptionSignal e `notElem` disabled = throwError e+          | otherwise                            = trapHandler newContext e+        disabled = [Inexact, Rounded, Subnormal]++-- | Return a new context with all signal flags cleared, all traps disabled+-- (IEEE 854 §7), using selectable precision (the IEEE 754 smallest and basic+-- formats correspond to precisions 'P7', 'P16', or 'P34'), and rounding half+-- even (IEEE 754 §4.3.3).+extendedDefaultContext :: Context p RoundHalfEven+extendedDefaultContext = newContext++-- | A representation of an exceptional condition+data Exception p r =+  Exception { exceptionSignal :: Signal+                                 -- ^ The signal raised by the exceptional+                                 -- condition+            , exceptionResult :: Decimal p r+                                 -- ^ The defined result for the exceptional+                                 -- condition+            }+  deriving Show++-- | A decimal arithmetic monad parameterized by the precision @p@ and+-- rounding algorithm @r@+newtype Arith p r a = Arith (ExceptT (Exception p r)+                             (State (Context p r)) a)++instance Functor (Arith p r) where+  fmap f (Arith s) = Arith (fmap f s)++instance Applicative (Arith p r) where+  pure = Arith . pure+  Arith f <*> Arith e = Arith (f <*> e)++instance Monad (Arith p r) where+  Arith e >>= f = Arith (e >>= g)+    where g x = let Arith t = f x in t++instance MonadError (Exception p r) (Arith p r) where+  throwError = Arith . throwError+  catchError (Arith e) f = Arith (catchError e g)+    where g x = let Arith t = f x in t++instance MonadState (Context p r) (Arith p r) where+  get = Arith   get+  put = Arith . put++-- | Evaluate an arithmetic computation in the given context and return the+-- final value (or exception) and resulting context.+runArith :: Arith p r a -> Context p r+         -> (Either (Exception p r) a, Context p r)+runArith (Arith e) = runState (runExceptT e)++-- | Evaluate an arithmetic computation in the given context and return the+-- final value or exception, discarding the resulting context.+evalArith :: Arith p r a -> Context p r -> Either (Exception p r) a+evalArith (Arith e) = evalState (runExceptT e)++-- $exceptional-conditions+--+-- Exceptional conditions are grouped into signals, which can be controlled+-- individually. A 'Context' contains a flag and a trap enabler (i.e. enabled+-- or disabled) for each 'Signal'.++data Signal+  = Clamped+    -- ^ Raised when the exponent of a result has been altered or constrained+    -- in order to fit the constraints of a specific concrete representation+  | DivisionByZero+    -- ^ Raised when a non-zero dividend is divided by zero+  | Inexact+    -- ^ Raised when a result is not exact (one or more non-zero coefficient+    -- digits were discarded during rounding)+  | InvalidOperation+    -- ^ Raised when a result would be undefined or impossible+  | Overflow+    -- ^ Raised when the exponent of a result is too large to be represented+  | Rounded+    -- ^ Raised when a result has been rounded (that is, some zero or non-zero+    -- coefficient digits were discarded)+  | Subnormal+    -- ^ Raised when a result is subnormal (its adjusted exponent is less than+    -- E/min/), before any rounding+  | Underflow+    -- ^ Raised when a result is both subnormal and inexact+  deriving (Eq, Enum, Bounded, Show)++-- | A group of signals can be manipulated as a set.+newtype Signals = Signals Int+                deriving Eq++instance Show Signals where+  showsPrec d sigs = showParen (d > 10) $+    showString "signals " . showsPrec 11 (signalList sigs)++instance Monoid Signals where+  mempty = Signals 0+  Signals x `mappend` Signals y = Signals (x .|. y)++-- | Create a set of signals from a singleton.+signal :: Signal -> Signals+signal = Signals . bit . fromEnum++-- | Create a set of signals from a list.+signals :: [Signal] -> Signals+signals = mconcat . map signal++-- | Enumerate the given set of signals.+signalList :: Signals -> [Signal]+signalList sigs = filter (`signalMember` sigs) [minBound..maxBound]++-- | Remove the first set of signals from the second.+unsignal :: Signals -> Signals -> Signals+unsignal (Signals u) (Signals ss) = Signals (ss .&. complement u)++-- | Determine whether a signal is a member of a set.+signalMember :: Signal -> Signals -> Bool+signalMember sig (Signals ss) = testBit ss (fromEnum sig)++-- | Set the given signal flag in the context of the current arithmetic+-- computation, and call the trap handler if the trap for this signal is+-- currently enabled.+raiseSignal :: Signal -> Decimal p r -> Arith p r (Decimal p r)+raiseSignal sig n = do+  ctx <- get+  let ctx' = ctx { flags = flags ctx <> signal sig }+  put ctx'+  trapHandler ctx' (Exception sig n)++-- | Clear the given signal flags from the context of the current arithmetic+-- computation.+clearFlags :: Signals -> Arith p r ()+clearFlags sigs = modify $ \ctx -> ctx { flags = unsignal sigs (flags ctx) }++-- | A trap handler function may return a substitute result for the operation+-- that caused the exceptional condition, or it may call 'throwError' to abort+-- the arithmetic computation (or pass control to an enclosing 'catchError'+-- handler).+type TrapHandler p r = Exception p r -> Arith p r (Decimal p r)++-- | Evaluate an arithmetic computation within a modified context that enables+-- the given signals to be trapped by the given handler. The previous trap+-- handler (and enabler state) will be restored during any trap, as well as+-- upon completion. Any existing trap handlers for signals not mentioned+-- remain in effect.+trap :: Signals -> TrapHandler p r -> Arith p r a -> Arith p r a+trap sigs handler arith = do+  origHandler <- gets trapHandler++  let newHandler e = wrapHandler origHandler $+        if exceptionSignal e `signalMember` sigs+        then handler e+        else origHandler e++  wrapHandler newHandler arith `catchError` \e -> do+    setHandler origHandler+    throwError e++  where wrapHandler :: TrapHandler p r -> Arith p r a -> Arith p r a+        wrapHandler handler arith = do+          prevHandler <- gets trapHandler+          setHandler handler+          r <- arith+          setHandler prevHandler+          return r++        setHandler :: TrapHandler p r -> Arith p r ()+        setHandler handler = modify $ \ctx -> ctx { trapHandler = handler }
+ src/Numeric/Decimal/Arithmetic.hs-boot view
@@ -0,0 +1,43 @@+-- -*- Haskell -*-++{-# LANGUAGE RoleAnnotations #-}++module Numeric.Decimal.Arithmetic+       ( Arith+       , newContext+       , evalArith+       , Signal(..)+       , raiseSignal+       , exceptionResult+       ) where++import Control.Monad.Except (ExceptT)+import Control.Monad.State (State)++import {-# SOURCE #-} Numeric.Decimal.Number++--instance Precision p => Precision (Arith p r a)++data Context p r+newContext :: Context p r++newtype Arith p r a = Arith (ExceptT (Exception p r) (State (Context p r)) a)+instance Functor (Arith p r)+instance Applicative (Arith p r)+instance Monad (Arith p r)+evalArith :: Arith p r a -> Context p r -> Either (Exception p r) a++data Signal+  = Clamped+  | DivisionByZero+  | Inexact+  | InvalidOperation+  | Overflow+  | Rounded+  | Subnormal+  | Underflow+raiseSignal :: Signal -> Decimal p r -> Arith p r (Decimal p r)++type role Exception phantom phantom+data Exception p r+exceptionResult :: Exception p r -> Decimal p r
src/Numeric/Decimal/Conversion.hs view
@@ -1,5 +1,5 @@ --- | The functions in this module implement conversions between 'Number' and+-- | The functions in this module implement conversions between 'Decimal' and -- 'String' as described in the /General Decimal Arithmetic Specification/. -- -- Because these functions are also used to implement 'Show' and 'Read' class@@ -28,11 +28,78 @@  import Numeric.Decimal.Number import Numeric.Decimal.Precision-import Numeric.Decimal.Rounding +{- $numeric-string-syntax++(The following description is from the+/General Decimal Arithmetic Specification/.)++Strings which are acceptable for conversion to the abstract representation of+numbers, or which might result from conversion from the abstract+representation to a string, are called /numeric strings/.++A /numeric string/ is a character string that describes either a+/finite number/ or a /special value/.++*   If it describes a /finite number/, it includes one or more decimal+    digits, with an optional decimal point. The decimal point may be embedded+    in the digits, or may be prefixed or suffixed to them. The group of+    digits (and optional point) thus constructed may have an optional sign+    (“@+@” or “@-@”) which must come before any digits or decimal point.++    The string thus described may optionally be followed by an “@E@”+    (indicating an exponential part), an optional sign, and an integer+    following the sign that represents a power of ten that is to be+    applied. The “@E@” may be in uppercase or lowercase.++*   If it describes a /special value/, it is one of the case-independent+    names “@Infinity@”, “@Inf@”, “@NaN@”, or “@sNaN@” (where the first two+    represent /infinity/ and the second two represent /quiet NaN/ and+    /signaling NaN/ respectively). The name may be preceded by an optional+    sign, as for finite numbers. If a NaN, the name may also be followed by+    one or more digits, which encode any diagnostic information.++No blanks or other white space characters are permitted in a numeric string.++== Examples++Some numeric strings are:++>     "0"          -- zero+>     "12"         -- a whole number+>    "-76"         -- a signed whole number+>     "12.70"      -- some decimal places+>     "+0.003"     -- a plus sign is allowed, too+>    "017."        -- the same as 17+>       ".5"       -- the same as 0.5+>     "4E+9"       -- exponential notation+>      "0.73e-7"   -- exponential notation, negative power+>     "Inf"        -- the same as Infinity+>     "-infinity"  -- the same as -Inf+>     "NaN"        -- not-a-Number+>     "NaN8275"    -- diagnostic NaN++== Notes++1. A single period alone or with a sign is not a valid numeric string.+2. A sign alone is not a valid numeric string.+3. Significant (after the decimal point) and insignificant leading zeros+   are permitted.+-}++{- $setup+>>> :load Harness+>>> import Numeric.Decimal.Conversion+-}++{- $doctest+prop> read' toNumber (show' toScientificString  x) == x+prop> read' toNumber (show' toEngineeringString x) == x+-}+ -- | Convert a number to a string, using scientific notation if an exponent is -- needed.-toScientificString :: Number p r -> ShowS+toScientificString :: Decimal p r -> ShowS toScientificString = showNumber exponential    where exponential :: Exponent -> String -> Exponent -> ShowS@@ -40,9 +107,72 @@                                         showString ds . showExponent e         exponential e     ds        _ = showString ds . showExponent e +{- $doctest-toScientificString++>>> show' toScientificString $ fromRep $ N (0,123,0)+"123"++>>> show' toScientificString $ fromRep $ N (1,123,0)+"-123"++>>> show' toScientificString $ fromRep $ N (0,123,1)+"1.23E+3"++>>> show' toScientificString $ fromRep $ N (0,123,3)+"1.23E+5"++>>> show' toScientificString $ fromRep $ N (0,123,-1)+"12.3"++>>> show' toScientificString $ fromRep $ N (0,123,-5)+"0.00123"++>>> show' toScientificString $ fromRep $ N (0,123,-10)+"1.23E-8"++>>> show' toScientificString $ fromRep $ N (1,123,-12)+"-1.23E-10"++>>> show' toScientificString $ fromRep $ N (0,0,0)+"0"++>>> show' toScientificString $ fromRep $ N (0,0,-2)+"0.00"++>>> show' toScientificString $ fromRep $ N (0,0,2)+"0E+2"++>>> show' toScientificString $ fromRep $ N (1,0,0)+"-0"++>>> show' toScientificString $ fromRep $ N (0,5,-6)+"0.000005"++>>> show' toScientificString $ fromRep $ N (0,50,-7)+"0.0000050"++>>> show' toScientificString $ fromRep $ N (0,5,-7)+"5E-7"++>>> show' toScientificString $ fromRep $ I 0+"Infinity"++>>> show' toScientificString $ fromRep $ I 1+"-Infinity"++>>> show' toScientificString $ fromRep $ Q (0,0)+"NaN"++>>> show' toScientificString $ fromRep $ Q (0,123)+"NaN123"++>>> show' toScientificString $ fromRep $ S (1,0)+"-sNaN"+-}+ -- | Convert a number to a string, using engineering notation if an exponent -- is needed.-toEngineeringString :: Number p r -> ShowS+toEngineeringString :: Decimal p r -> ShowS toEngineeringString = showNumber exponential    where exponential :: Exponent -> String -> Exponent -> ShowS@@ -70,8 +200,32 @@          shift _ e     ds              = showString ds . showExponent e +{- $doctest-toEngineeringString++>>> show' toEngineeringString $ fromRep $ N (0,123,1)+"1.23E+3"++>>> show' toEngineeringString $ fromRep $ N (0,123,3)+"123E+3"++>>> show' toEngineeringString $ fromRep $ N (0,123,-10)+"12.3E-9"++>>> show' toEngineeringString $ fromRep $ N (1,123,-12)+"-123E-12"++>>> show' toEngineeringString $ fromRep $ N (0,7,-7)+"700E-9"++>>> show' toEngineeringString $ fromRep $ N (0,7,1)+"70"++>>> show' toEngineeringString $ fromRep $ N (0,0,1)+"0.00E+3"+-}+ showNumber :: (Exponent -> String -> Exponent -> ShowS)-           -> Number p r -> ShowS+           -> Decimal p r -> ShowS showNumber exponential num = signStr . case num of   Num { coefficient = c, exponent = e }     | e <= 0 && ae >= -6 -> nonExponential@@ -114,21 +268,21 @@ -- | Convert a string to a number, as defined by its abstract representation. -- The string is expected to conform to the numeric string syntax described -- here.-toNumber :: (Precision p, Rounding r) => ReadP (Number p r)-toNumber = round <$> (parseSign flipSign <*> parseNumericString)+toNumber :: ReadP (Decimal PInfinite r)+toNumber = parseSign flipSign <*> parseNumericString    where parseSign :: (a -> a) -> ReadP (a -> a)         parseSign negate = char '-' *> pure negate           <|> optional (char '+') *> pure id -        parseNumericString :: ReadP (Number p r)+        parseNumericString :: ReadP (Decimal p r)         parseNumericString = parseNumericValue <|> parseNaN -        parseNumericValue :: ReadP (Number p r)+        parseNumericValue :: ReadP (Decimal p r)         parseNumericValue = parseDecimalPart <*> option 0 parseExponentPart           <|> parseInfinity -        parseDecimalPart :: ReadP (Exponent -> Number p r)+        parseDecimalPart :: ReadP (Exponent -> Decimal p r)         parseDecimalPart = digitsWithPoint <|> digitsWithOptionalPoint            where digitsWithPoint = do@@ -136,10 +290,9 @@                   char '.'                   fracDigits <- many parseDigit                   return $ \e ->-                    Num { context = defaultContext-                        , sign = Pos+                    Num { sign        = Pos                         , coefficient = readDigits (digits ++ fracDigits)-                        , exponent = e - fromIntegral (length fracDigits)+                        , exponent    = e - fromIntegral (length fracDigits)                         }                  digitsWithOptionalPoint = fractionalDigits <|> wholeDigits@@ -148,18 +301,16 @@                   char '.'                   fracDigits <- many1 parseDigit                   return $ \e ->-                    Num { context = defaultContext-                        , sign = Pos+                    Num { sign        = Pos                         , coefficient = readDigits fracDigits-                        , exponent = e - fromIntegral (length fracDigits)+                        , exponent    = e - fromIntegral (length fracDigits)                         }                  wholeDigits = do                   digits <- many1 parseDigit-                  return $ \e -> Num { context = defaultContext-                                     , sign = Pos+                  return $ \e -> Num { sign        = Pos                                      , coefficient = readDigits digits-                                     , exponent = e+                                     , exponent    = e                                      }          parseExponentPart :: ReadP Exponent@@ -167,25 +318,25 @@           parseString "E"           parseSign negate <*> (readDigits <$> many1 parseDigit) -        parseInfinity :: ReadP (Number p r)+        parseInfinity :: ReadP (Decimal p r)         parseInfinity = do           parseString "Inf"           optional $ parseString "inity"-          return Inf { context = defaultContext, sign = Pos }+          return Inf { sign = Pos } -        parseNaN :: ReadP (Number p r)+        parseNaN :: ReadP (Decimal p r)         parseNaN = parseQNaN <|> parseSNaN -        parseQNaN :: ReadP (Number p r)+        parseQNaN :: ReadP (Decimal p r)         parseQNaN = do           p <- parseNaNPayload-          return QNaN { context = defaultContext, sign = Pos, payload = p }+          return QNaN { sign = Pos, payload = p } -        parseSNaN :: ReadP (Number p r)+        parseSNaN :: ReadP (Decimal p r)         parseSNaN = do           parseString "s"           p <- parseNaNPayload-          return SNaN { context = defaultContext, sign = Pos, payload = p }+          return SNaN { sign = Pos, payload = p }          parseNaNPayload :: ReadP Payload         parseNaNPayload = do@@ -201,57 +352,74 @@         readDigits :: Num c => [Int] -> c         readDigits = foldl' (\a b -> a * 10 + fromIntegral b) 0 --- $numeric-string-syntax------ (The following description is from the--- /General Decimal Arithmetic Specification/.)------ Strings which are acceptable for conversion to the abstract representation--- of numbers, or which might result from conversion from the abstract--- representation to a string, are called /numeric strings/.------ A /numeric string/ is a character string that describes either a /finite number/ or a /special value/.------ *   If it describes a /finite number/, it includes one or more decimal---     digits, with an optional decimal point. The decimal point may be embedded---     in the digits, or may be prefixed or suffixed to them. The group of---     digits (and optional point) thus constructed may have an optional sign---     (“@+@” or “@-@”) which must come before any digits or decimal point.------     The string thus described may optionally be followed by an “@E@”---     (indicating an exponential part), an optional sign, and an integer---     following the sign that represents a power of ten that is to be---     applied. The “@E@” may be in uppercase or lowercase.------ *   If it describes a /special value/, it is one of the case-independent---     names “@Infinity@”, “@Inf@”, “@NaN@”, or “@sNaN@” (where the first two---     represent /infinity/ and the second two represent /quiet NaN/ and---     /signaling NaN/ respectively). The name may be preceded by an optional---     sign, as for finite numbers. If a NaN, the name may also be followed by---     one or more digits, which encode any diagnostic information.------ No blanks or other white space characters are permitted in a numeric string.------ == Examples------ Some numeric strings are:------ >     "0"          -- zero--- >     "12"         -- a whole number--- >    "-76"         -- a signed whole number--- >     "12.70"      -- some decimal places--- >     "+0.003"     -- a plus sign is allowed, too--- >    "017."        -- the same as 17--- >       ".5"       -- the same as 0.5--- >     "4E+9"       -- exponential notation--- >      "0.73e-7"   -- exponential notation, negative power--- >     "Inf"        -- the same as Infinity--- >     "-infinity"  -- the same as -Inf--- >     "NaN"        -- not-a-Number--- >     "NaN8275"    -- diagnostic NaN------ == Notes------ 1. A single period alone or with a sign is not a valid numeric string.--- 2. A sign alone is not a valid numeric string.--- 3. Significant (after the decimal point) and insignificant leading zeros are permitted.+{- $doctest-toNumber++>>> toRep $ read' toNumber "0"+N (0,0,0)++>>> toRep $ read' toNumber "0.00"+N (0,0,-2)++>>> toRep $ read' toNumber "123"+N (0,123,0)++>>> toRep $ read' toNumber "-123"+N (1,123,0)++>>> toRep $ read' toNumber "1.23E3"+N (0,123,1)++>>> toRep $ read' toNumber "1.23E+3"+N (0,123,1)++>>> toRep $ read' toNumber "12.3E+7"+N (0,123,6)++>>> toRep $ read' toNumber "12.0"+N (0,120,-1)++>>> toRep $ read' toNumber "12.3"+N (0,123,-1)++>>> toRep $ read' toNumber "0.00123"+N (0,123,-5)++>>> toRep $ read' toNumber "-1.23E-12"+N (1,123,-14)++>>> toRep $ read' toNumber "1234.5E-4"+N (0,12345,-5)++>>> toRep $ read' toNumber "-0"+N (1,0,0)++>>> toRep $ read' toNumber "-0.00"+N (1,0,-2)++>>> toRep $ read' toNumber "0E+7"+N (0,0,7)++>>> toRep $ read' toNumber "-0E-7"+N (1,0,-7)++>>> toRep $ read' toNumber "inf"+I 0++>>> toRep $ read' toNumber "+inFiniTy"+I 0++>>> toRep $ read' toNumber "-Infinity"+I 1++>>> toRep $ read' toNumber "NaN"+Q (0,0)++>>> toRep $ read' toNumber "-NAN"+Q (1,0)++>>> toRep $ read' toNumber "SNaN"+S (0,0)++XXX toRep $ read' toNumber "Fred"+Q (0,0)+-}
src/Numeric/Decimal/Conversion.hs-boot view
@@ -8,8 +8,7 @@ import Text.ParserCombinators.ReadP (ReadP)  import {-# SOURCE #-} Numeric.Decimal.Number-import                Numeric.Decimal.Precision (Precision)-import {-# SOURCE #-} Numeric.Decimal.Rounding (Rounding)+import                Numeric.Decimal.Precision -toScientificString :: Number p r -> ShowS-toNumber :: (Precision p, Rounding r) => ReadP (Number p r)+toScientificString :: Decimal p r -> ShowS+toNumber :: ReadP (Decimal PInfinite r)
src/Numeric/Decimal/Number.hs view
@@ -10,7 +10,7 @@        , Exponent        , Payload -       , Number(..)+       , Decimal(..)        , zero        , one        , negativeOne@@ -20,7 +20,7 @@         , flipSign        , cast-       , excessDigits+       , toBool         , isPositive        , isNegative@@ -28,36 +28,33 @@        , isZero        , isNormal        , isSubnormal--       , Context(..)-       , TrapHandler-       , defaultContext-       , mergeContexts--       , Signal(..)-       , raiseSignal        ) where  import Prelude hiding (exponent, round) -import Data.Bits (bit, complement, testBit, (.&.), (.|.))+import Data.Char (isSpace) import Data.Coerce (coerce)-import Data.Monoid ((<>)) import Data.Ratio (numerator, denominator, (%)) import Numeric.Natural (Natural) import Text.ParserCombinators.ReadP (readP_to_S)  import {-# SOURCE #-} Numeric.Decimal.Conversion+import {-# SOURCE #-} Numeric.Decimal.Arithmetic import                Numeric.Decimal.Precision-import {-# SOURCE #-} Numeric.Decimal.Rounding+import                Numeric.Decimal.Rounding  import {-# SOURCE #-} qualified Numeric.Decimal.Operation as Op  import qualified GHC.Real -data Sign = Pos | Neg-          deriving (Eq, Enum, Show)+{- $setup+>>> :load Harness+-} +data Sign = Pos  -- ^ Positive or non-negative+          | Neg  -- ^ Negative+          deriving (Eq, Enum)+ negateSign :: Sign -> Sign negateSign Pos = Neg negateSign Neg = Pos@@ -68,181 +65,365 @@ xorSigns Neg Pos = Neg xorSigns Neg Neg = Pos -signFactor :: Num a => Sign -> a-signFactor Pos =  1-signFactor Neg = -1- signFunc :: Num a => Sign -> a -> a signFunc Pos = id signFunc Neg = negate -type Coefficient = Natural-type Exponent = Integer+signMatch :: (Num a, Eq a) => a -> Sign+signMatch x = case signum x of+  -1 -> Neg+  _  -> Pos -type Payload = Coefficient+type Coefficient = Natural+type Exponent    = Int+type Payload     = Coefficient  -- | A decimal floating point number with selectable precision and rounding -- algorithm-data Number p r-  = Num  { context     :: Context p r-         , sign        :: Sign+data Decimal p r+  = Num  { sign        :: Sign          , coefficient :: Coefficient          , exponent    :: Exponent          }-  | Inf  { context     :: Context p r-         , sign        :: Sign+  | Inf  { sign        :: Sign          }-  | QNaN { context     :: Context p r-         , sign        :: Sign+  | QNaN { sign        :: Sign          , payload     :: Payload          }-  | SNaN { context     :: Context p r-         , sign        :: Sign+  | SNaN { sign        :: Sign          , payload     :: Payload          } -instance Precision p => Precision (Number p r) where-  precision = precision . numberPrecision-    where numberPrecision :: Number p r -> p-          numberPrecision = undefined--instance Show (Number p r) where+instance Show (Decimal p r) where   showsPrec d n = showParen (d > 0 && isNegative n) $ toScientificString n -instance (Precision p, Rounding r) => Read (Number p r) where-  readsPrec _ = readP_to_S toNumber+instance (Precision p, Rounding r) => Read (Decimal p r) where+  readsPrec _ str = [ (cast n, s)+                    | (n, s) <- readParen False+                      (readP_to_S toNumber . dropWhile isSpace) str ] -instance (Precision p, Rounding r) => Eq (Number p r) where-  x == y = case x `Op.compare` y of+{- $doctest-Read+>>> fmap toRep (read "Just 123" :: Maybe GeneralDecimal)+Just (N (0,123,0))++>>> fmap toRep (read "Just (-12.0)" :: Maybe GeneralDecimal)+Just (N (1,120,-1))+-}++instance Precision p => Precision (Decimal p r) where+  precision = precision . decimalPrecision+    where decimalPrecision :: Decimal p r -> p+          decimalPrecision = undefined++evalOp :: (Precision p, Rounding r) => Arith p r (Decimal p r) -> Decimal p r+evalOp op = either exceptionResult id $ evalArith op newContext++type GeneralDecimal = Decimal PInfinite RoundHalfEven++instance Eq (Decimal p r) where+  x == y = case evalOp (x `Op.compare` y) :: GeneralDecimal of     Num { coefficient = 0 } -> True     _                       -> False -instance (Precision p, Rounding r) => Ord (Number p r) where-  x `compare` y = case x `Op.compare` y of+instance (Precision p, Rounding r) => Ord (Decimal p r) where+  x `compare` y = case evalOp (x `Op.compare` y) :: GeneralDecimal of     Num { coefficient = 0 } -> EQ     Num { sign = Neg      } -> LT     Num { sign = Pos      } -> GT     _                       -> GT  -- match Prelude behavior for NaN -  x < y = case x `Op.compare` y of+  x < y = case evalOp (x `Op.compare` y) :: GeneralDecimal of     Num { sign = Neg      } -> True     _                       -> False -  x <= y = case x `Op.compare` y of+  x <= y = case evalOp (x `Op.compare` y) :: GeneralDecimal of     Num { sign = Neg      } -> True     Num { coefficient = 0 } -> True     _                       -> False -  x > y = case x `Op.compare` y of+  x > y = case evalOp (x `Op.compare` y) :: GeneralDecimal of     Num { coefficient = 0 } -> False     Num { sign = Pos      } -> True     _                       -> False -  x >= y = case x `Op.compare` y of+  x >= y = case evalOp (x `Op.compare` y) :: GeneralDecimal of     Num { sign = Pos      } -> True     _                       -> False -  max nan@SNaN{} _ = nan-  max _ nan@SNaN{} = nan-  max nan@QNaN{} _ = nan-  max _ nan@QNaN{} = nan-  max x y-    | x >= y    = x-    | otherwise = y+  max x y = evalOp (Op.max x y)+  min x y = evalOp (Op.min x y) -  min nan@SNaN{} _ = nan-  min _ nan@SNaN{} = nan-  min nan@QNaN{} _ = nan-  min _ nan@QNaN{} = nan-  min x y-    | x < y     = x-    | otherwise = y+{- $doctest-Ord+prop> x > y ==> max x y == x && max y x == (x :: BasicDecimal)+prop> x < y ==> min x y == x && min y x == (x :: BasicDecimal) -instance (FinitePrecision p, Rounding r) => Enum (Number p r) where+prop> max x y == x ==> x >= y+prop> max x y == y ==> y >= x+prop> min x y == x ==> x <= y+prop> min x y == y ==> y <= x+-}++instance (Precision p, Rounding r) => Enum (Decimal p r) where+  succ x = evalOp (x `Op.add`      one)+  pred x = evalOp (x `Op.subtract` one)+   toEnum = fromIntegral-  fromEnum = truncate -instance (Precision p, Rounding r) => Num (Number p r) where-  (+)    = Op.add-  (-)    = Op.subtract-  (*)    = Op.multiply-  negate = Op.minus-  abs    = Op.abs+  fromEnum Num { sign = s, coefficient = c, exponent = e }+    | e >= 0    = signFunc s (fromIntegral   c  *      10^  e  )+    | otherwise = signFunc s (fromIntegral $ c `quot` (10^(-e)))+  fromEnum _ = 0 +  enumFrom       x     = enumFromWith x one+  enumFromThen   x y   = let i = y - x+                         in x : enumFromWith y i+  enumFromTo     x   z = takeWhile (<= z) $ enumFromWith x one+  enumFromThenTo x y z = let i = y - x+                             cmp | i < 0     = (>=)+                                 | otherwise = (<=)+                         in takeWhile (`cmp` z) $ x : enumFromWith y i++enumFromWith :: (Precision p, Rounding r)+             => Decimal p r -> Decimal p r -> [Decimal p r]+enumFromWith x i = x : enumFromWith (x + i) i++{- $doctest-Enum+>>> [0, 0.1 .. 2] :: [BasicDecimal]+[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2.0]++>>> [2, 1.9 .. 0] :: [BasicDecimal]+[2,1.9,1.8,1.7,1.6,1.5,1.4,1.3,1.2,1.1,1.0,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0]++>>> [1.7 .. 5.7] :: [BasicDecimal]+[1.7,2.7,3.7,4.7,5.7]+-}++instance (Precision p, Rounding r) => Num (Decimal p r) where+  x + y = evalOp (x `Op.add`      y)+  x - y = evalOp (x `Op.subtract` y)+  x * y = evalOp (x `Op.multiply` y)++  negate = evalOp . Op.minus+  abs    = evalOp . Op.abs+   signum n = case n of     Num { coefficient = 0 } -> zero     Num { sign = s        } -> one { sign = s }     Inf { sign = s        } -> one { sign = s }     _                       -> n -  fromInteger x = Num { context     = defaultContext-                      , sign        = sx-                      , coefficient = fromInteger (abs x)-                      , exponent    = 0-                      }-    where sx = case signum x of-            -1 -> Neg-            _  -> Pos+  fromInteger x = cast+    Num { sign        = signMatch x+        , coefficient = fromInteger (abs x)+        , exponent    = 0+        } -instance (Precision p, Rounding r) => Real (Number p r) where+{- $doctest-Num+prop> x + x == x * (2 :: GeneralDecimal)+prop> isFinite x ==> x - x == (0 :: GeneralDecimal)+prop> isFinite x ==> x + negate x == (0 :: GeneralDecimal)+prop> abs x >= (0 :: GeneralDecimal)++prop> abs x * signum x == (x :: GeneralDecimal)+-}++instance (Precision p, Rounding r) => Real (Decimal p r) where   toRational Num { sign = s, coefficient = c, exponent = e }-    | e >= 0    = fromInteger (signFactor s * fromIntegral c * 10^e)-    | otherwise = (signFactor s * fromIntegral c) % 10^(-e)+    | e >= 0    = fromInteger $ signFunc s (fromIntegral c * 10^e)+    | otherwise = signFunc s (fromIntegral c) % 10^(-e)   toRational n = signFunc (sign n) $ case n of     Inf{} -> GHC.Real.infinity     _     -> GHC.Real.notANumber -instance (FinitePrecision p, Rounding r) => Fractional (Number p r) where-  (/) = Op.divide-  fromRational r = fromInteger (numerator r) / fromInteger (denominator r)+instance (FinitePrecision p, Rounding r) => Fractional (Decimal p r) where+  x / y = evalOp (x `Op.divide` y)+  fromRational r = let n = fromInteger (numerator   r) :: GeneralDecimal+                       d = fromInteger (denominator r) :: GeneralDecimal+                   in coerce n / coerce d -instance (FinitePrecision p, Rounding r) => RealFrac (Number p r) where+{- $doctest-Fractional+prop> (4.14 :: Decimal P2 RoundHalfUp)   == 4.1+prop> (4.15 :: Decimal P2 RoundHalfUp)   == 4.2+prop> (4.15 :: Decimal P2 RoundHalfDown) == 4.1+prop> (4.15 :: Decimal P2 RoundHalfEven) == 4.2+prop> (4.25 :: Decimal P2 RoundHalfEven) == 4.2+prop> (4.35 :: Decimal P2 RoundHalfEven) == 4.4+prop> (4.45 :: Decimal P2 RoundHalfEven) == 4.4+-}++instance (FinitePrecision p, Rounding r) => RealFrac (Decimal p r) where   properFraction x@Num { sign = s, coefficient = c, exponent = e }     | e < 0     = (n, f)-    | otherwise = (signFactor s * fromIntegral c * 10^e, zero)-    where n = signFactor s * fromIntegral q-          f = x { coefficient = r, exponent = -(fromIntegral $ numDigits r) }+    | otherwise = (signFunc s (fromIntegral c * 10^e), zero)+    where n = signFunc s (fromIntegral q)+          f = x { coefficient = r }           (q, r) = c `quotRem` (10^(-e))   properFraction nan = (0, nan) --- | A 'Number' representing the value zero-zero :: Number p r-zero = Num { context     = defaultContext-           , sign        = Pos+{- $doctest-RealFrac+prop> let (n,f) = properFraction (x :: BasicDecimal) in x == fromIntegral n + f+prop> let (n,f) = properFraction (x :: BasicDecimal) in (x < 0 && n <= 0) || (x >= 0 && n >= 0)+prop> let (n,f) = properFraction (x :: BasicDecimal) in (x < 0 && f <= 0) || (x >= 0 && f >= 0)+prop> let (n,f) = properFraction (x :: BasicDecimal) in isFinite f ==> abs f < 1+-}++-- | Compute an infinite series to maximum precision.+infiniteSeries :: (FinitePrecision p, Rounding r)+               => [Decimal p r] -> Decimal p r+infiniteSeries = series zero+  where series n (x:xs)+          | n' == n   = n'+          | otherwise = series n' xs+          where n' = n + x+        series n []   = n++-- | Compute the arcsine of the argument to maximum precision using series+-- expansion.+arcsine :: (FinitePrecision p, Rounding r) => Decimal p r -> Decimal p r+arcsine x = infiniteSeries (x : series 1 2 x 3)+  where series n d x i =+          let x' = x * x2+          in (n * x') / (d * i) : series (n * i) (d * (i + one)) x' (i + two)+        x2 = x * x++-- | Compute π to maximum precision using the arcsine series expansion.+seriesPi :: FinitePrecision p => Decimal p RoundHalfEven+seriesPi = 6 * arcsine oneHalf++-- | Cast a number with two additional digits of precision down to a number+-- with the desired precision.+castDown :: (Precision p, Rounding r)+         => Decimal (PPlus1 (PPlus1 p)) a -> Decimal p r+castDown = cast++notyet :: String -> a+notyet = error . (++ ": not yet implemented")++instance (FinitePrecision p, Rounding r) => Floating (Decimal p r) where+  pi = castDown seriesPi++  exp   = notyet "exp"+  log   = notyet "log"++  sin   = notyet "sin"+  cos   = notyet "cos"+  asin  = notyet "asin"+  acos  = notyet "acos"+  atan  = notyet "atan"+  sinh  = notyet "sinh"+  cosh  = notyet "cosh"+  asinh = notyet "asinh"+  acosh = notyet "acosh"+  atanh = notyet "atanh"++{- $doctest-Floating+prop> realToFrac (pi :: ExtendedDecimal P16) == (pi :: Double)+-}++instance (FinitePrecision p, Rounding r) => RealFloat (Decimal p r) where+  floatRadix  _ = 10+  floatDigits x = let Just p = precision x in p+  floatRange  _ = (minBound, maxBound)  -- ?++  decodeFloat x = case x of+    Num  { sign = s, coefficient = c, exponent = e } -> (m, n)+      where m = signFunc s (fromIntegral c)+            n = fromIntegral e+    Inf  { sign = s              } -> (special s 0, maxBound    )+    QNaN { sign = s, payload = p } -> (special s p, minBound    )+    SNaN { sign = s, payload = p } -> (special s p, minBound + 1)+    where special :: Sign -> Coefficient -> Integer+          special s v = signFunc s (pp + fromIntegral v)+          pp = 10 ^ floatDigits x :: Integer++  encodeFloat m n = x+    where x | am >= pp  = special+            | otherwise = cast Num { sign        = signMatch m+                                   , coefficient = fromInteger am+                                   , exponent    = fromIntegral n+                                   }+          special+            | n == maxBound     = Inf  { sign = signMatch m }+            | n == minBound     = QNaN { sign = signMatch m, payload = p }+            | otherwise         = SNaN { sign = signMatch m, payload = p }+            where p = fromInteger (am - pp)+          am = abs m              :: Integer+          pp = 10 ^ floatDigits x :: Integer++  isNaN x = case x of+    QNaN{} -> True+    SNaN{} -> True+    _      -> False++  isInfinite x = case x of+    Inf{} -> True+    _     -> False++  isDenormalized = isSubnormal++  isNegativeZero x = case x of+    Num { sign = Neg, coefficient = 0 } -> True+    _                                   -> False++  isIEEE _ = True++{- $doctest-RealFloat+prop> uncurry encodeFloat (decodeFloat x) == (x :: BasicDecimal)+prop> isFinite x ==> significand x * fromInteger (floatRadix x) ^^ Prelude.exponent x == (x :: BasicDecimal)+prop> Prelude.exponent (0 :: BasicDecimal) == 0+prop> isFinite x && x /= 0 ==> Prelude.exponent (x :: BasicDecimal) == snd (decodeFloat x) + floatDigits x++prop> isNegativeZero (read "-0" :: BasicDecimal) == True+prop> isNegativeZero (read "+0" :: BasicDecimal) == False+prop> x /= 0 ==> isNegativeZero (x :: BasicDecimal) == False+-}++-- | A 'Decimal' representing the value zero+zero :: Decimal p r+zero = Num { sign        = Pos            , coefficient = 0            , exponent    = 0            } --- | A 'Number' representing the value one-one :: Number p r+-- | A 'Decimal' representing the value ½+oneHalf :: Decimal p r+oneHalf = zero { coefficient = 5, exponent = -1 }++-- | A 'Decimal' representing the value one+one :: Decimal p r one = zero { coefficient = 1 } --- | A 'Number' representing the value negative one-negativeOne :: Number p r+-- | A 'Decimal' representing the value two+two :: Decimal p r+two = zero { coefficient = 2 }++-- | A 'Decimal' representing the value negative one+negativeOne :: Decimal p r negativeOne = one { sign = Neg } --- | A 'Number' representing the value positive infinity-infinity :: Number p r-infinity = Inf { context = defaultContext, sign = Pos }+-- | A 'Decimal' representing the value positive infinity+infinity :: Decimal p r+infinity = Inf { sign = Pos } --- | A 'Number' representing undefined results-qNaN :: Number p r-qNaN = QNaN { context = defaultContext, sign = Pos, payload = 0 }+-- | A 'Decimal' representing undefined results+qNaN :: Decimal p r+qNaN = QNaN { sign = Pos, payload = 0 } --- | A signaling 'Number' representing undefined results-sNaN :: Number p r-sNaN = SNaN { context = defaultContext, sign = Pos, payload = 0 }+-- | A signaling 'Decimal' representing undefined results+sNaN :: Decimal p r+sNaN = SNaN { sign = Pos, payload = 0 } --- | Negate the given 'Number' by directly flipping its sign.-flipSign :: Number p r -> Number p r+-- | Negate the given 'Decimal' by directly flipping its sign.+flipSign :: Decimal p r -> Decimal p r flipSign n = n { sign = negateSign (sign n) } --- | Cast a 'Number' to another precision and/or rounding algorithm,+-- | Cast a 'Decimal' to another precision and/or rounding algorithm, -- immediately rounding if necessary to the new precision using the new -- algorithm.-cast :: (Precision p, Rounding r) => Number a b -> Number p r-cast = round . coerce+cast :: (Precision p, Rounding r) => Decimal a b -> Decimal p r+cast = evalOp . round . coerce +-- | Return the number of decimal digits of the argument. numDigits :: Coefficient -> Int numDigits x   | x <         10 = 1@@ -256,73 +437,71 @@   | x < 1000000000 = 9   | otherwise      = 9 + numDigits (x `quot` 1000000000) -excessDigits :: Precision p => Number p r -> Maybe Int-excessDigits x@Num { coefficient = c } = precision x >>= excess-  where excess p-          | d > p     = Just (d - p)-          | otherwise = Nothing-          where d = numDigits c-excessDigits _ = Nothing- maxCoefficient :: Precision p => p -> Maybe Coefficient maxCoefficient p = (\d -> 10 ^ d - 1) <$> precision p --- | Is the sign of the given 'Number' positive?-isPositive :: Number p r -> Bool+-- | Is the sign of the given 'Decimal' positive?+isPositive :: Decimal p r -> Bool isPositive n = case sign n of   Pos -> True   Neg -> False --- | Is the sign of the given 'Number' negative?-isNegative :: Number p r -> Bool+-- | Is the sign of the given 'Decimal' negative?+isNegative :: Decimal p r -> Bool isNegative n = case sign n of   Neg -> True   Pos -> False --- | Does the given 'Number' represent a finite value?-isFinite :: Number p r -> Bool+-- | Does the given 'Decimal' represent a finite value?+isFinite :: Decimal p r -> Bool isFinite Num{} = True isFinite _     = False --- | Does the given 'Number' represent the value zero?-isZero :: Number p r -> Bool+-- | Does the given 'Decimal' represent the value zero?+isZero :: Decimal p r -> Bool isZero Num { coefficient = 0 } = True isZero _                       = False --- | Is the given 'Number' normal?-isNormal :: Precision p => Number p r -> Bool+-- | Is the given 'Decimal' normal?+isNormal :: Precision p => Decimal p r -> Bool isNormal n-  | isFinite n && not (isZero n) &&-    maybe True (adjustedExponent n >=) (eMin n) = True-  | otherwise                                   = False+  | isFinite n && not (isZero n) = maybe True (adjustedExponent n >=) (eMin n)+  | otherwise                    = False --- | Is the given 'Number' subnormal?-isSubnormal :: Precision p => Number p r -> Bool+-- | Is the given 'Decimal' subnormal?+isSubnormal :: Precision p => Decimal p r -> Bool isSubnormal n-  | isFinite n && not (isZero n) &&-    maybe False (adjustedExponent n <) (eMin n) = True-  | otherwise                                   = False+  | isFinite n && not (isZero n) = maybe False (adjustedExponent n <) (eMin n)+  | otherwise                    = False +-- | Return 'False' if the argument is zero or NaN, and 'True' otherwise.+toBool :: Decimal p r -> Bool+toBool Num { coefficient = c }+  | c == 0    = False+  | otherwise = True+toBool Inf{}  = True+toBool _      = False+ -- | Upper limit on the absolute value of the exponent-eLimit :: Precision p => Number p r -> Maybe Exponent-eLimit = eMax+eLimit :: Precision p => p -> Maybe Exponent+eLimit = eMax -- ?  -- | Minimum value of the adjusted exponent-eMin :: Precision p => Number p r -> Maybe Exponent+eMin :: Precision p => p -> Maybe Exponent eMin n = (1 -) <$> eMax n  -- | Maximum value of the adjusted exponent-eMax :: Precision p => Number p r -> Maybe Exponent+eMax :: Precision p => p -> Maybe Exponent eMax n = subtract 1 . (10 ^) . numDigits <$> base   where mlength = precision n                    :: Maybe Int-        base = (10 *) . fromIntegral <$> mlength :: Maybe Natural+        base = (10 *) . fromIntegral <$> mlength :: Maybe Coefficient  -- | Minimum value of the exponent for subnormal results-eTiny :: Precision p => Number p r -> Maybe Exponent+eTiny :: Precision p => p -> Maybe Exponent eTiny n = (-) <$> eMin n <*> (fromIntegral . subtract 1 <$> precision n)  -- | Range of permissible exponent values-eRange :: Precision p => Number p r -> Maybe (Exponent, Exponent)+eRange :: Precision p => Decimal p r -> Maybe (Exponent, Exponent) eRange n@Num { coefficient = c } = range <$> eLimit n   where range :: Exponent -> (Exponent, Exponent)         range lim = (-lim - clm1 + 1, lim - clm1)@@ -330,72 +509,8 @@         clm1 = fromIntegral (clength - 1) :: Exponent eRange _ = Nothing -adjustedExponent :: Number p r -> Exponent+adjustedExponent :: Decimal p r -> Exponent adjustedExponent Num { coefficient = c, exponent = e } =   e + fromIntegral (clength - 1)   where clength = numDigits c :: Int adjustedExponent _ = error "adjustedExponent: not a finite number"--type TrapHandler p r = Signal -> Number p r -> Number p r--data Context p r = Context { signalFlags :: Signals-                           , trapHandler :: TrapHandler p r-                           }--instance Precision p => Precision (Context p r) where-  precision = precision . contextPrecision-    where contextPrecision :: Context p r -> p-          contextPrecision = undefined--defaultContext :: Context p r-defaultContext = Context mempty (const id)--setSignal :: Signal -> Context p r -> Context p r-setSignal sig cxt = cxt { signalFlags = signalFlags cxt <> signal sig }--modifyContext :: (Context p r -> Context p r) -> Number p r -> Number p r-modifyContext f n = n { context = f (context n) }--mergeContexts :: Context p r -> Context p r -> Context p r-mergeContexts cxt1 cxt2 =-  cxt1 { signalFlags = signalFlags cxt1 <> signalFlags cxt2 }--data Signal-  = Clamped-  | DivisionByZero-  | Inexact-  | InvalidOperation-  | Overflow-  | Rounded-  | Subnormal-  | Underflow-  deriving (Enum, Bounded, Show)--newtype Signals = Signals Int--instance Show Signals where-  showsPrec d sigs = showParen (d > 10) $-    showString "signals " . showsPrec 11 (signalList sigs)--instance Monoid Signals where-  mempty = Signals 0-  Signals x `mappend` Signals y = Signals (x .|. y)--signal :: Signal -> Signals-signal = Signals . bit . fromEnum--unsignal :: Signal -> Signals -> Signals-unsignal sig (Signals ss) = Signals $ ss .&. complement (bit $ fromEnum sig)--signals :: [Signal] -> Signals-signals = foldr (\s n -> signal s <> n) mempty--signalList :: Signals -> [Signal]-signalList sigs = filter (testSignal sigs) [minBound..maxBound]--testSignal :: Signals -> Signal -> Bool-testSignal (Signals ss) = testBit ss . fromEnum--raiseSignal :: Signal -> Number p r -> Number p r-raiseSignal sig n = let n' = modifyContext (setSignal sig) n-                    in trapHandler (context n') sig n'
src/Numeric/Decimal/Number.hs-boot view
@@ -3,8 +3,38 @@ {-# LANGUAGE RoleAnnotations #-}  module Numeric.Decimal.Number-       ( Number+       ( Sign(..)+       , Decimal(..)+       , Coefficient+       , numDigits        ) where -type role Number phantom phantom-data Number p r+import Numeric.Natural (Natural)++import Numeric.Decimal.Precision++data Sign = Pos | Neg+instance Eq Sign++type Coefficient = Natural+type Exponent    = Int+type Payload     = Coefficient++type role Decimal phantom phantom+data Decimal p r+  = Num  { sign        :: Sign+         , coefficient :: Coefficient+         , exponent    :: Exponent+         }+  | Inf  { sign        :: Sign+         }+  | QNaN { sign        :: Sign+         , payload     :: Payload+         }+  | SNaN { sign        :: Sign+         , payload     :: Payload+         }++instance Precision p => Precision (Decimal p r)++numDigits :: Coefficient -> Int
src/Numeric/Decimal/Operation.hs view
@@ -1,202 +1,1054 @@ --- | Eventually most or all of the arithmetic operations described in the--- /General Decimal Arithmetic Specification/ will be provided here. For now,--- the operations are mostly limited to those exposed through various class--- methods.------ It is not usually necessary to import this module.--module Numeric.Decimal.Operation-       ( abs-       , add-       , subtract-       , multiply-       , divide-       , plus-       , minus-       , compare-       ) where--import Prelude hiding (abs, compare, exponent, round, subtract)-import qualified Prelude--import                Numeric.Decimal.Number-import                Numeric.Decimal.Precision-import {-# SOURCE #-} Numeric.Decimal.Rounding--invalidOperation :: Number p r -> Number p r-invalidOperation n = raiseSignal InvalidOperation qNaN { context = context n }--toQNaN :: Number p r -> Number p r-toQNaN SNaN { context = t, sign = s, payload = p } =-  QNaN { context = t, sign = s, payload = p }-toQNaN n@QNaN{} = n-toQNaN n = qNaN { context = context n, sign = sign n }--toQNaN2 :: Number p r -> Number p r -> Number p r-toQNaN2 nan@SNaN{} _ = toQNaN nan-toQNaN2 _ nan@SNaN{} = toQNaN nan-toQNaN2 nan@QNaN{} _ = nan-toQNaN2 _ nan@QNaN{} = nan-toQNaN2 n _          = toQNaN n---- | Add two operands.-add :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r-add Num { context = xt, sign = xs, coefficient = xc, exponent = xe }-    Num { context = yt, sign = ys, coefficient = yc, exponent = ye } = round rn--  where rn = Num { context = rt, sign = rs, coefficient = rc, exponent = re }-        rt = mergeContexts xt yt-        rs | rc /= 0                     = if xac > yac then xs else ys-           | xs == Neg && ys == Neg      = Neg-           | xs /= ys && isRoundFloor rn = Neg-           | otherwise                   = Pos-        rc | xs == ys  = xac + yac-           | xac > yac = xac - yac-           | otherwise = yac - xac-        re = min xe ye-        (xac, yac) | xe == ye  = (xc, yc)-                   | xe >  ye  = (xc * 10^n, yc)-                   | otherwise = (xc, yc * 10^n)-          where n = Prelude.abs (xe - ye)--add inf@Inf { context = xt, sign = xs } Inf { context = yt, sign = ys }-  | xs == ys  = inf { context = mergeContexts xt yt }-  | otherwise = invalidOperation inf { context = mergeContexts xt yt }-add inf@Inf{} Num{} = inf-add Num{} inf@Inf{} = inf-add x y             = toQNaN2 x y---- | Subtract the second operand from the first.-subtract :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r-subtract x = add x . flipSign---- | Unary minus (negation)-minus :: (Precision p, Rounding r) => Number p r -> Number p r-minus x = zero { exponent = exponent x } `subtract` x---- | Unary plus-plus :: (Precision p, Rounding r) => Number p r -> Number p r-plus x = zero { exponent = exponent x } `add` x---- | Multiply two operands.-multiply :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r-multiply Num { context = xt, sign = xs, coefficient = xc, exponent = xe }-         Num { context = yt, sign = ys, coefficient = yc, exponent = ye } =-  round rn--  where rn = Num { context = rt, sign = rs, coefficient = rc, exponent = re }-        rt = mergeContexts xt yt-        rs = xorSigns xs ys-        rc = xc * yc-        re = xe + ye--multiply Inf { context = xt, sign = xs } Inf { context = yt, sign = ys } =-  Inf { context = mergeContexts xt yt, sign = xorSigns xs ys }-multiply Inf { context = xt, sign = xs } Num { context = yt, sign = ys } =-  Inf { context = mergeContexts xt yt, sign = xorSigns xs ys }-multiply Num { context = xt, sign = xs } Inf { context = yt, sign = ys } =-  Inf { context = mergeContexts xt yt, sign = xorSigns xs ys }-multiply x y = toQNaN2 x y---- | Divide the first dividend operand by the second divisor using long division.-divide :: (FinitePrecision p, Rounding r)-       => Number p r -> Number p r -> Number p r-divide dividend@Num{ sign = xs } Num { coefficient = 0, sign = ys }-  | isZero dividend = invalidOperation qNaN-  | otherwise       = raiseSignal DivisionByZero-                        infinity { sign = xorSigns xs ys }-divide Num { context = xt, sign = xs, coefficient = xc, exponent = xe }-       Num { context = yt, sign = ys, coefficient = yc, exponent = ye } =-  result--  where rn = Num { context = rt, sign = rs, coefficient = rc, exponent = re }-        rt = mergeContexts xt yt-        rs = xorSigns xs ys-        (rc, rem, dv, adjust) = longDivision xc yc p-        re = xe - (ye + adjust)-        Just p = precision rn-        result-          | rem == 0  = rn-          | otherwise = round $ case (rem * 2) `Prelude.compare` dv of-              LT -> rn { coefficient = rc * 10 + 1, exponent = re - 1 }-              EQ -> rn { coefficient = rc * 10 + 5, exponent = re - 1 }-              GT -> rn { coefficient = rc * 10 + 9, exponent = re - 1 }--divide Inf{} Inf{} = invalidOperation qNaN-divide Inf { context = xt, sign = xs } Num { context = yt, sign = ys } =-  Inf { context = mergeContexts xt yt, sign = xorSigns xs ys }-divide Num { context = xt, sign = xs } Inf { context = yt, sign = ys } =-  zero { context = mergeContexts xt yt, sign = xorSigns xs ys }-divide x y = toQNaN2 x y--type Dividend  = Coefficient-type Divisor   = Coefficient-type Quotient  = Coefficient-type Remainder = Coefficient--longDivision :: Dividend -> Divisor -> Int-             -> (Quotient, Remainder, Divisor, Exponent)-longDivision 0  dv _ = (0, 0, dv, 0)-longDivision dd dv p = step1 dd dv 0--  where step1 dd dv adjust-          | dd <       dv = step1 (dd * 10)  dv       (adjust + 1)-          | dd >= 10 * dv = step1  dd       (dv * 10) (adjust - 1)-          | otherwise     = step2  dd        dv        adjust--        step2 = step3 0--        step3 r dd dv adjust-          | dv <= dd                 = step3 (r +  1) (dd - dv) dv  adjust-          | (dd == 0 && adjust >= 0) ||-            numDigits r == p         = step4  r        dd       dv  adjust-          | otherwise                = step3 (r * 10) (dd * 10) dv (adjust + 1)--        step4 = (,,,)---- | If the operand is negative, the result is the same as using the 'minus'--- operation on the operand. Otherwise, the result is the same as using the--- 'plus' operation on the operand.-abs :: (Precision p, Rounding r) => Number p r -> Number p r-abs x-  | isNegative x = minus x-  | otherwise    = plus  x---- | Compare the values of two operands numerically, returning @-1@ if the--- first is less than the second, @0@ if they are equal, or @1@ if the first--- is greater than the second.-compare :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r-compare x@Num{} y@Num{} = (nzp $ xn `subtract` yn) { context = rt }--  where (xn, yn) | sign x /= sign y = (nzp x, nzp y)-                 | otherwise        = (x, y)--        rt = mergeContexts (context x) (context y)--        nzp :: Number p r -> Number p r-        nzp Num { context = t, sign = s, coefficient = c }-          | c == 0    = zero        { context = t }-          | s == Pos  = one         { context = t }-          | otherwise = negativeOne { context = t }-        nzp Inf { context = t, sign = s }-          | s == Pos  = one         { context = t }-          | otherwise = negativeOne { context = t }-        nzp n = toQNaN n--compare Inf { context = xt, sign = xs } Inf { context = yt, sign = ys }-  | xs == ys  = zero        { context = rt }-  | xs == Neg = negativeOne { context = rt }-  | otherwise = one         { context = rt }-  where rt = mergeContexts xt yt-compare Inf { context = xt, sign = xs } Num { context = yt }-  | xs == Neg = negativeOne { context = rt }-  | otherwise = one         { context = rt }-  where rt = mergeContexts xt yt-compare Num { context = xt } Inf { context = yt, sign = ys }-  | ys == Pos = negativeOne { context = rt }-  | otherwise = one         { context = rt }-  where rt = mergeContexts xt yt-compare nan@SNaN{} _ = invalidOperation nan-compare _ nan@SNaN{} = invalidOperation nan-compare x y          = toQNaN2 x y+{- | Eventually most or all of the arithmetic operations described in the+/General Decimal Arithmetic Specification/ will be provided here. For now, the+operations are mostly limited to those exposed through various class methods.++It is suggested to import this module qualified to avoid "Prelude" name+clashes:++> import qualified Numeric.Decimal.Operation as Op++Note that it is not usually necessary to import this module unless you want to+use operations unavailable through class methods, or you need precise control+over the handling of exceptional conditions.+-}+module Numeric.Decimal.Operation+       ( -- * Arithmetic operations+         -- $arithmetic-operations++         abs+       , add+       , subtract+       , compare+         -- compareSignal+       , divide+         -- divideInteger+         -- exp+         -- fusedMultiplyAdd+         -- ln+         -- log10+       , max+       , maxMagnitude+       , min+       , minMagnitude+       , minus+       , plus+       , multiply+         -- nextMinus+         -- nextPlus+         -- nextToward+         -- power+         -- quantize+         , reduce+         -- remainder+         -- remainderNear+         -- roundToIntegralExact+         -- roundToIntegralValue+         -- squareRoot++         -- * Miscellaneous operations+         -- $miscellaneous-operations++         -- and+         , canonical+         , class_, Class(..), Sign(..), Subclass(..)+         -- compareTotal+         -- compareTotalMagnitude+         , copy+         , copyAbs+         , copyNegate+         , copySign+         -- invert+         , isCanonical+         , isFinite+         , isInfinite+         , isNaN+         , isNormal+         , isQNaN+         , isSigned+         , isSNaN+         , isSubnormal+         , isZero+         -- logb+         -- or+         , radix+         -- rotate+         , sameQuantum+         -- scaleb+         -- shift+         -- xor+       ) where++import Prelude hiding (abs, compare, exponent, isInfinite, isNaN, max, min,+                       round, subtract)+import qualified Prelude++import Data.Coerce (coerce)++import Numeric.Decimal.Arithmetic+import Numeric.Decimal.Number hiding (isFinite, isNormal, isSubnormal, isZero)+import Numeric.Decimal.Precision+import Numeric.Decimal.Rounding++import qualified Numeric.Decimal.Number as Number++{- $setup+>>> :load Harness+-}++finitePrecision :: FinitePrecision p => Decimal p r -> Int+finitePrecision n = let Just p = precision n in p++roundingAlg :: Rounding r => Arith p r a -> RoundingAlgorithm+roundingAlg = rounding . arithRounding+  where arithRounding :: Arith p r a -> r+        arithRounding = undefined++result :: (Precision p, Rounding r) => Decimal p r -> Arith p r (Decimal p r)+result = round  -- ...+--  | maybe False (numDigits c >) (precision r) = undefined++invalidOperation :: Decimal a b -> Arith p r (Decimal p r)+invalidOperation n = raiseSignal InvalidOperation qNaN++toQNaN :: Decimal a b -> Decimal p r+toQNaN SNaN { sign = s, payload = p } = QNaN { sign = s, payload = p }+toQNaN n@QNaN{}                       = coerce n+toQNaN n                              = qNaN { sign = sign n }++toQNaN2 :: Decimal a b -> Decimal c d -> Decimal p r+toQNaN2 nan@SNaN{} _ = toQNaN nan+toQNaN2 _ nan@SNaN{} = toQNaN nan+toQNaN2 nan@QNaN{} _ = coerce nan+toQNaN2 _ nan@QNaN{} = coerce nan+toQNaN2 n _          = toQNaN n++-- $arithmetic-operations+--+-- This section describes the arithmetic operations on, and some other+-- functions of, numbers, including subnormal numbers, negative zeros, and+-- special values (see also IEEE 754 §5 and §6).++{- $doctest-special-values+>>> op2 Op.add "Infinity" "1"+Infinity++>>> op2 Op.add "NaN" "1"+NaN++>>> op2 Op.add "NaN" "Infinity"+NaN++>>> op2 Op.subtract "1" "Infinity"+-Infinity++>>> op2 Op.multiply "-1" "Infinity"+-Infinity++>>> op2 Op.subtract "-0" "0"+-0++>>> op2 Op.multiply "-1" "0"+-0++>>> op2 Op.divide "1" "0"+Infinity++>>> op2 Op.divide "1" "-0"+-Infinity++>>> op2 Op.divide "-1" "0"+-Infinity+-}++-- | 'add' takes two operands. If either operand is a /special value/ then the+-- general rules apply.+--+-- Otherwise, the operands are added.+--+-- The result is then rounded to /precision/ digits if necessary, counting+-- from the most significant digit of the result.+add :: (Precision p, Rounding r)+    => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+add Num { sign = xs, coefficient = xc, exponent = xe }+    Num { sign = ys, coefficient = yc, exponent = ye } = sum++  where sum = result Num { sign = rs, coefficient = rc, exponent = re }+        rs | rc /= 0                       = if xac > yac then xs else ys+           | xs == Neg && ys == Neg        = Neg+           | xs /= ys &&+             roundingAlg sum == RoundFloor = Neg+           | otherwise                     = Pos+        rc | xs == ys  = xac + yac+           | xac > yac = xac - yac+           | otherwise = yac - xac+        re = Prelude.min xe ye+        (xac, yac) | xe == ye  = (xc, yc)+                   | xe >  ye  = (xc * 10^n, yc)+                   | otherwise = (xc, yc * 10^n)+          where n = Prelude.abs (xe - ye)++add inf@Inf { sign = xs } Inf { sign = ys }+  | xs == ys  = return (coerce inf)+  | otherwise = invalidOperation inf+add inf@Inf{} Num{} = return (coerce inf)+add Num{} inf@Inf{} = return (coerce inf)+add x y             = return (toQNaN2 x y)++{- $doctest-add+>>> op2 Op.add "12" "7.00"+19.00++>>> op2 Op.add "1E+2" "1E+4"+1.01E+4+-}++-- | 'subtract' takes two operands. If either operand is a /special value/+-- then the general rules apply.+--+-- Otherwise, the operands are added after inverting the /sign/ used for the+-- second operand.+--+-- The result is then rounded to /precision/ digits if necessary, counting+-- from the most significant digit of the result.+subtract :: (Precision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+subtract x = add x . flipSign++{- $doctest-subtract+>>> op2 Op.subtract "1.3" "1.07"+0.23++>>> op2 Op.subtract "1.3" "1.30"+0.00++>>> op2 Op.subtract "1.3" "2.07"+-0.77+-}++-- | 'minus' takes one operand, and corresponds to the prefix minus operator+-- in programming languages.+--+-- Note that the result of this operation is affected by context and may set+-- /flags/. The 'copyNegate' operation may be used instead of 'minus' if this+-- is not desired.+minus :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)+minus x = zero { exponent = exponent x } `subtract` x++{- $doctest-minus+>>> op1 Op.minus "1.3"+-1.3++>>> op1 Op.minus "-1.3"+1.3+-}++-- | 'plus' takes one operand, and corresponds to the prefix plus operator in+-- programming languages.+--+-- Note that the result of this operation is affected by context and may set+-- /flags/.+plus :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)+plus x = zero { exponent = exponent x } `add` x++{- $doctest-plus+>>> op1 Op.plus "1.3"+1.3++>>> op1 Op.plus "-1.3"+-1.3+-}++-- | 'multiply' takes two operands. If either operand is a /special value/+-- then the general rules apply. Otherwise, the operands are multiplied+-- together (“long multiplication”), resulting in a number which may be as+-- long as the sum of the lengths of the two operands.+--+-- The result is then rounded to /precision/ digits if necessary, counting+-- from the most significant digit of the result.+multiply :: (Precision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+multiply Num { sign = xs, coefficient = xc, exponent = xe }+         Num { sign = ys, coefficient = yc, exponent = ye } = result rn++  where rn = Num { sign = rs, coefficient = rc, exponent = re }+        rs = xorSigns xs ys+        rc = xc * yc+        re = xe + ye++multiply Inf { sign = xs } Inf { sign = ys } =+  return Inf { sign = xorSigns xs ys }+multiply Inf { sign = xs } Num { sign = ys } =+  return Inf { sign = xorSigns xs ys }+multiply Num { sign = xs } Inf { sign = ys } =+  return Inf { sign = xorSigns xs ys }+multiply x y = return (toQNaN2 x y)++{- $doctest-multiply+>>> op2 Op.multiply "1.20" "3"+3.60++>>> op2 Op.multiply "7" "3"+21++>>> op2 Op.multiply "0.9" "0.8"+0.72++>>> op2 Op.multiply "0.9" "-0"+-0.0++>>> op2 Op.multiply "654321" "654321"+4.28135971E+11+-}++-- | 'divide' takes two operands. If either operand is a /special value/ then the general rules apply.+--+-- Otherwise, if the divisor is zero then either the Division undefined+-- condition is raised (if the dividend is zero) and the result is NaN, or the+-- Division by zero condition is raised and the result is an Infinity with a+-- sign which is the exclusive or of the signs of the operands.+--+-- Otherwise, a “long division” is effected.+--+-- The result is then rounded to /precision/ digits, if necessary, according+-- to the /rounding/ algorithm and taking into account the remainder from the+-- division.+divide :: (FinitePrecision p, Rounding r)+       => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+divide dividend@Num{ sign = xs } Num { coefficient = 0, sign = ys }+  | Number.isZero dividend = invalidOperation qNaN+  | otherwise              = raiseSignal DivisionByZero+                        infinity { sign = xorSigns xs ys }+divide Num { sign = xs, coefficient = xc, exponent = xe }+       Num { sign = ys, coefficient = yc, exponent = ye } = quotient++  where quotient = result =<< answer+        rn = Num { sign = rs, coefficient = rc, exponent = re }+        rs = xorSigns xs ys+        (rc, rem, dv, adjust) = longDivision xc yc (finitePrecision rn)+        re = xe - (ye + adjust)+        answer+          | rem == 0  = return rn+          | otherwise = round $ case (rem * 2) `Prelude.compare` dv of+              LT -> rn { coefficient = rc * 10 + 1, exponent = re - 1 }+              EQ -> rn { coefficient = rc * 10 + 5, exponent = re - 1 }+              GT -> rn { coefficient = rc * 10 + 9, exponent = re - 1 }++divide Inf{} Inf{} = invalidOperation qNaN+divide Inf { sign = xs } Num { sign = ys } =+  return Inf { sign = xorSigns xs ys }+divide Num { sign = xs } Inf { sign = ys } =+  return zero { sign = xorSigns xs ys }+divide x y = return (toQNaN2 x y)++{- $doctest-divide+>>> op2 Op.divide "1" "3"+0.333333333++>>> op2 Op.divide "2" "3"+0.666666667++>>> op2 Op.divide "5" "2"+2.5++>>> op2 Op.divide "1" "10"+0.1++>>> op2 Op.divide "12" "12"+1++>>> op2 Op.divide "8.00" "2"+4.00++>>> op2 Op.divide "2.400" "2.0"+1.20++>>> op2 Op.divide "1000" "100"+10++>>> op2 Op.divide "1000" "1"+1000++>>> op2 Op.divide "2.40E+6" "2"+1.20E+6+-}++type Dividend  = Coefficient+type Divisor   = Coefficient+type Quotient  = Coefficient+type Remainder = Dividend++longDivision :: Dividend -> Divisor -> Int+             -> (Quotient, Remainder, Divisor, Exponent)+longDivision 0  dv _ = (0, 0, dv, 0)+longDivision dd dv p = step1 dd dv 0++  where step1 :: Dividend -> Divisor -> Exponent+              -> (Quotient, Remainder, Divisor, Exponent)+        step1 dd dv adjust+          | dd <       dv = step1 (dd * 10)  dv       (adjust + 1)+          | dd >= 10 * dv = step1  dd       (dv * 10) (adjust - 1)+          | otherwise     = step2  dd        dv        adjust++        step2 :: Dividend -> Divisor -> Exponent+              -> (Quotient, Remainder, Divisor, Exponent)+        step2 = step3 0++        step3 :: Quotient -> Dividend -> Divisor -> Exponent+              -> (Quotient, Remainder, Divisor, Exponent)+        step3 r dd dv adjust+          | dv <= dd                 = step3 (r +  1) (dd - dv) dv  adjust+          | (dd == 0 && adjust >= 0) ||+            numDigits r == p         = step4  r        dd       dv  adjust+          | otherwise                = step3 (r * 10) (dd * 10) dv (adjust + 1)++        step4 :: Quotient -> Remainder -> Divisor -> Exponent+              -> (Quotient, Remainder, Divisor, Exponent)+        step4 = (,,,)++-- | 'abs' takes one operand. If the operand is negative, the result is the+-- same as using the 'minus' operation on the operand. Otherwise, the result+-- is the same as using the 'plus' operation on the operand.+--+-- Note that the result of this operation is affected by context and may set+-- /flags/. The 'copyAbs' operation may be used if this is not desired.+abs :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)+abs x+  | isNegative x = minus x+  | otherwise    = plus  x++{- $doctest-abs+>>> op1 Op.abs "2.1"+2.1++>>> op1 Op.abs "-100"+100++>>> op1 Op.abs "101.5"+101.5++>>> op1 Op.abs "-101.5"+101.5+-}++-- | 'compare' takes two operands and compares their values numerically. If+-- either operand is a /special value/ then the general rules apply. No flags+-- are set unless an operand is a signaling NaN.+--+-- Otherwise, the operands are compared, returning @-1@ if the first is less+-- than the second, @0@ if they are equal, or @1@ if the first is greater than+-- the second.+compare :: (Precision p, Rounding r)+        => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+compare x@Num{} y@Num{} = nzp <$> (xn `subtract` yn)++  where (xn, yn) | sign x /= sign y = (nzp x, nzp y)+                 | otherwise        = (x, y)++        nzp :: Decimal p r -> Decimal p r+        nzp Num { sign = s, coefficient = c }+          | c == 0    = zero+          | s == Pos  = one+          | otherwise = negativeOne+        nzp Inf { sign = s }+          | s == Pos  = one+          | otherwise = negativeOne+        nzp n = toQNaN n++compare Inf { sign = xs } Inf { sign = ys }+  | xs == ys  = return zero+  | xs == Neg = return negativeOne+  | otherwise = return one+compare Inf { sign = xs } Num { }+  | xs == Neg = return negativeOne+  | otherwise = return one+compare Num { } Inf { sign = ys }+  | ys == Pos = return negativeOne+  | otherwise = return one+compare nan@SNaN{} _ = invalidOperation nan+compare _ nan@SNaN{} = invalidOperation nan+compare x y          = return (toQNaN2 x y)++{- $doctest-compare+>>> op2 Op.compare "2.1" "3"+-1++>>> op2 Op.compare "2.1" "2.1"+0++>>> op2 Op.compare "2.1" "2.10"+0++>>> op2 Op.compare "3" "2.1"+1++>>> op2 Op.compare "2.1" "-3"+1++>>> op2 Op.compare "-3" "2.1"+-1+-}++-- | 'max' takes two operands, compares their values numerically, and returns+-- the maximum. If either operand is a NaN then the general rules apply,+-- unless one is a quiet NaN and the other is numeric, in which case the+-- numeric operand is returned.+max :: (Precision p, Rounding r)+    => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+max x y = snd <$> minMax id x y++{- $doctest-max+>>> op2 Op.max "3" "2"+3++>>> op2 Op.max "-10" "3"+3++>>> op2 Op.max "1.0" "1"+1++>>> op2 Op.max "7" "NaN"+7+-}++-- | 'maxMagnitude' takes two operands and compares their values numerically+-- with their /sign/ ignored and assumed to be 0.+--+-- If, without signs, the first operand is the larger then the original first+-- operand is returned (that is, with the original sign). If, without signs,+-- the second operand is the larger then the original second operand is+-- returned. Otherwise the result is the same as from the 'max' operation.+maxMagnitude :: (Precision p, Rounding r)+             => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+maxMagnitude x y = snd <$> minMax withoutSign x y++-- | 'min' takes two operands, compares their values numerically, and returns+-- the minimum. If either operand is a NaN then the general rules apply,+-- unless one is a quiet NaN and the other is numeric, in which case the+-- numeric operand is returned.+min :: (Precision p, Rounding r)+    => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+min x y = fst <$> minMax id x y++{- $doctest-min+>>> op2 Op.min "3" "2"+2++>>> op2 Op.min "-10" "3"+-10++>>> op2 Op.min "1.0" "1"+1.0++>>> op2 Op.min "7" "NaN"+7+-}++-- | 'minMagnitude' takes two operands and compares their values numerically+-- with their /sign/ ignored and assumed to be 0.+--+-- If, without signs, the first operand is the smaller then the original first+-- operand is returned (that is, with the original sign). If, without signs,+-- the second operand is the smaller then the original second operand is+-- returned. Otherwise the result is the same as from the 'min' operation.+minMagnitude :: (Precision p, Rounding r)+             => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+minMagnitude x y = fst <$> minMax withoutSign x y++-- | Ordering function for 'min', 'minMagnitude', 'max', and 'maxMagnitude':+-- returns the original arguments as (smaller, larger) when the given function+-- is applied to them.+minMax :: (Precision p, Rounding r)+       => (Decimal a b -> Decimal a b) -> Decimal a b -> Decimal a b+       -> Arith p r (Decimal a b, Decimal a b)+minMax _ x@Num{}  QNaN{} = return (x, x)+minMax _ x@Inf{}  QNaN{} = return (x, x)+minMax _  QNaN{} y@Num{} = return (y, y)+minMax _  QNaN{} y@Inf{} = return (y, y)++minMax f x y = do+  c <- f x `compare` f y+  return $ case c of+    Num { coefficient = 0 } -> case (sign x, sign y) of+      (Neg, Pos) -> (x, y)+      (Pos, Neg) -> (y, x)+      (Pos, Pos) -> case (x, y) of+        (Num { exponent = xe }, Num { exponent = ye }) | xe > ye -> (y, x)+        _ -> (x, y)+      (Neg, Neg) -> case (x, y) of+        (Num { exponent = xe }, Num { exponent = ye }) | xe < ye -> (y, x)+        _ -> (x, y)+    Num { sign = Pos } -> (y, x)+    Num { sign = Neg } -> (x, y)+    nan -> let nan' = coerce nan in (nan', nan')+++withoutSign :: Decimal p r -> Decimal p r+withoutSign n = n { sign = Pos }++-- | 'reduce' takes one operand. It has the same semantics as the 'plus'+-- operation, except that if the final result is finite it is reduced to its+-- simplest form, with all trailing zeros removed and its sign preserved.+reduce :: (Precision p, Rounding r) => Decimal a b -> Arith p r (Decimal p r)+reduce n = reduce' <$> plus n+  where reduce' n@Num { coefficient = c, exponent = e }+          | c == 0 =         n {                  exponent = 0     }+          | r == 0 = reduce' n { coefficient = q, exponent = e + 1 }+          where (q, r) = c `quotRem` 10+        reduce' n = n++{- $doctest-reduce+>>> op1 Op.reduce "2.1"+2.1++>>> op1 Op.reduce "-2.0"+-2++>>> op1 Op.reduce "1.200"+1.2++>>> op1 Op.reduce "-120"+-1.2E+2++>>> op1 Op.reduce "120.00"+1.2E+2++>>> op1 Op.reduce "0.00"+0+-}++-- $miscellaneous-operations+--+-- This section describes miscellaneous operations on decimal numbers,+-- including non-numeric comparisons, sign and other manipulations, and+-- logical operations.++-- | 'canonical' takes one operand. The result has the same value as the+-- operand but always uses a /canonical/ encoding. The definition of+-- /canonical/ is implementation-defined; if more than one internal encoding+-- for a given NaN, Infinity, or finite number is possible then one+-- “preferred” encoding is deemed canonical. This operation then returns the+-- value using that preferred encoding.+--+-- If all possible operands have just one internal encoding each, then+-- 'canonical' always returns the operand unchanged (that is, it has the same+-- effect as 'copy'). This operation is unaffected by context and is quiet –+-- no /flags/ are changed in the context.+canonical :: Decimal a b -> Arith p r (Decimal a b)+canonical = return++{- $doctest-canonical+>>> op1 Op.canonical "2.50"+2.50+-}++-- | 'class_' takes one operand. The result is an indication of the /class/ of+-- the operand, where the class is one of ten possibilities, corresponding to+-- one of the strings @"sNaN"@ (signaling NaN), @\"NaN"@ (quiet NaN),+-- @"-Infinity"@ (negative infinity), @"-Normal"@ (negative normal finite+-- number), @"-Subnormal"@ (negative subnormal finite number), @"-Zero"@+-- (negative zero), @"+Zero"@ (non-negative zero), @"+Subnormal"@ (positive+-- subnormal finite number), @"+Normal"@ (positive normal finite number), or+-- @"+Infinity"@ (positive infinity). This operation is quiet; no /flags/ are+-- changed in the context.+--+-- Note that unlike the special values in the model, the sign of any NaN is+-- ignored in the classification, as required by IEEE 754.+class_ :: Precision a => Decimal a b -> Arith p r Class+class_ n = return $ case n of+  Num {} | Number.isZero n      -> Class (sign n) ZeroClass+         | Number.isSubnormal n -> Class (sign n) SubnormalClass+         | otherwise            -> Class (sign n) NormalClass+  Inf {}                        -> Class (sign n) InfinityClass+  QNaN{}                        -> Class  Pos     NaNClass+  SNaN{}                        -> Class  Neg     NaNClass++data Class = Class Sign Subclass deriving Eq++data Subclass = ZeroClass       -- ^ Zero+              | NormalClass     -- ^ Normal finite number+              | SubnormalClass  -- ^ Subnormal finite number+              | InfinityClass   -- ^ Infinity+              | NaNClass        -- ^ Not a number (quiet or signaling)+              deriving Eq++instance Show Class where+  show c = case c of+    Class Pos s@NaNClass ->       showSubclass s+    Class Neg s@NaNClass -> 's' : showSubclass s+    Class Pos s          -> '+' : showSubclass s+    Class Neg s          -> '-' : showSubclass s++    where showSubclass s = case s of+            ZeroClass      -> "Zero"+            NormalClass    -> "Normal"+            SubnormalClass -> "Subnormal"+            InfinityClass  -> "Infinity"+            NaNClass       -> "NaN"++{- $doctest-class_+>>> op1 Op.class_ "Infinity"++Infinity++>>> op1 Op.class_ "1E-10"++Normal++>>> op1 Op.class_ "2.50"++Normal++>>> op1 Op.class_ "0.1E-999"++Subnormal++>>> op1 Op.class_ "0"++Zero++>>> op1 Op.class_ "-0"+-Zero++>>> op1 Op.class_ "-0.1E-999"+-Subnormal++>>> op1 Op.class_ "-1E-10"+-Normal++>>> op1 Op.class_ "-2.50"+-Normal++>>> op1 Op.class_ "-Infinity"+-Infinity++>>> op1 Op.class_ "NaN"+NaN++>>> op1 Op.class_ "-NaN"+NaN++>>> op1 Op.class_ "sNaN"+sNaN+-}++-- | 'copy' takes one operand. The result is a copy of the operand. This+-- operation is unaffected by context and is quiet – no /flags/ are changed in+-- the context.+copy :: Decimal a b -> Arith p r (Decimal a b)+copy = return++{- $doctest-copy+>>> op1 Op.copy "2.1"+2.1++>>> op1 Op.copy "-1.00"+-1.00+-}++-- | 'copyAbs' takes one operand. The result is a copy of the operand with the+-- /sign/ set to 0. Unlike the 'abs' operation, this operation is unaffected+-- by context and is quiet – no /flags/ are changed in the context.+copyAbs :: Decimal a b -> Arith p r (Decimal a b)+copyAbs n = return n { sign = Pos }++{- $doctest-copyAbs+>>> op1 Op.copyAbs "2.1"+2.1++>>> op1 Op.copyAbs "-100"+100+-}++-- | 'copyNegate' takes one operand. The result is a copy of the operand with+-- the /sign/ inverted (a /sign/ of 0 becomes 1 and vice versa). Unlike the+-- 'minus' operation, this operation is unaffected by context and is quiet –+-- no /flags/ are changed in the context.+copyNegate :: Decimal a b -> Arith p r (Decimal a b)+copyNegate n = return n { sign = negateSign (sign n) }++{- $doctest-copyNegate+>>> op1 Op.copyNegate "101.5"+-101.5++>>> op1 Op.copyNegate "-101.5"+101.5+-}++-- | 'copySign' takes two operands. The result is a copy of the first operand+-- with the /sign/ set to be the same as the /sign/ of the second+-- operand. This operation is unaffected by context and is quiet – no /flags/+-- are changed in the context.+copySign :: Decimal a b -> Decimal c d -> Arith p r (Decimal a b)+copySign n m = return n { sign = sign m }++{- $doctest-copySign+>>> op2 Op.copySign  "1.50"  "7.33"+1.50++>>> op2 Op.copySign "-1.50"  "7.33"+1.50++>>> op2 Op.copySign  "1.50" "-7.33"+-1.50++>>> op2 Op.copySign "-1.50" "-7.33"+-1.50+-}++-- | 'isCanonical' takes one operand. The result is 1 if the operand is+-- /canonical/; otherwise it is 0. The definition of /canonical/ is+-- implementation-defined; if more than one internal encoding for a given NaN,+-- Infinity, or finite number is possible then one “preferred” encoding is+-- deemed canonical. This operation then tests whether the internal encoding+-- is that preferred encoding.+--+-- If all possible operands have just one internal encoding each, then+-- 'isCanonical' always returns 1. This operation is unaffected by context and+-- is quiet – no /flags/ are changed in the context.+isCanonical :: Decimal a b -> Arith p r (Decimal p r)+isCanonical _ = return one++{- $doctest-isCanonical+>>> op1 Op.isCanonical "2.50"+1+-}++-- | 'isFinite' takes one operand. The result is 1 if the operand is neither+-- infinite nor a NaN (that is, it is a normal number, a subnormal number, or+-- a zero); otherwise it is 0. This operation is unaffected by context and is+-- quiet – no /flags/ are changed in the context.+isFinite :: Decimal a b -> Arith p r (Decimal p r)+isFinite n = return $ case n of+  Num{} -> one+  _     -> zero++{- $doctest-isFinite+>>> op1 Op.isFinite "2.50"+1++>>> op1 Op.isFinite "-0.3"+1++>>> op1 Op.isFinite "0"+1++>>> op1 Op.isFinite "Inf"+0++>>> op1 Op.isFinite "NaN"+0+-}++-- | 'isInfinite' takes one operand. The result is 1 if the operand is an+-- Infinity; otherwise it is 0. This operation is unaffected by context and is+-- quiet – no /flags/ are changed in the context.+isInfinite :: Decimal a b -> Arith p r (Decimal p r)+isInfinite n = return $ case n of+  Inf{} -> one+  _     -> zero++{- $doctest-isInfinite+>>> op1 Op.isInfinite "2.50"+0++>>> op1 Op.isInfinite "-Inf"+1++>>> op1 Op.isInfinite "NaN"+0+-}++-- | 'isNaN' takes one operand. The result is 1 if the operand is a NaN (quiet+-- or signaling); otherwise it is 0. This operation is unaffected by context+-- and is quiet – no /flags/ are changed in the context.+isNaN :: Decimal a b -> Arith p r (Decimal p r)+isNaN n = return $ case n of+  QNaN{} -> one+  SNaN{} -> one+  _      -> zero++{- $doctest-isNaN+>>> op1 Op.isNaN "2.50"+0++>>> op1 Op.isNaN "NaN"+1++>>> op1 Op.isNaN "-sNaN"+1+-}++-- | 'isNormal' takes one operand. The result is 1 if the operand is a+-- positive or negative /normal number/; otherwise it is 0. This operation is+-- quiet; no /flags/ are changed in the context.+isNormal :: Precision a => Decimal a b -> Arith p r (Decimal p r)+isNormal n = return $ case n of+  _ | Number.isNormal n -> one+    | otherwise         -> zero++{- $doctest-isNormal+>>> op1 Op.isNormal "2.50"+1++>>> op1 Op.isNormal "0.1E-999"+0++>>> op1 Op.isNormal "0.00"+0++>>> op1 Op.isNormal "-Inf"+0++>>> op1 Op.isNormal "NaN"+0+-}++-- | 'isQNaN' takes one operand. The result is 1 if the operand is a quiet+-- NaN; otherwise it is 0. This operation is unaffected by context and is+-- quiet – no /flags/ are changed in the context.+isQNaN :: Decimal a b -> Arith p r (Decimal p r)+isQNaN n = return $ case n of+  QNaN{} -> one+  _      -> zero++{- $doctest-isQNaN+>>> op1 Op.isQNaN "2.50"+0++>>> op1 Op.isQNaN "NaN"+1++>>> op1 Op.isQNaN "sNaN"+0+-}++-- | 'isSigned' takes one operand. The result is 1 if the /sign/ of the+-- operand is 1; otherwise it is 0. This operation is unaffected by context+-- and is quiet – no /flags/ are changed in the context.+isSigned :: Decimal a b -> Arith p r (Decimal p r)+isSigned n = return $ case sign n of+  Neg -> one+  Pos -> zero++{- $doctest-isSigned+>>> op1 Op.isSigned "2.50"+0++>>> op1 Op.isSigned "-12"+1++>>> op1 Op.isSigned "-0"+1+-}++-- | 'isSNaN' takes one operand. The result is 1 if the operand is a signaling+-- NaN; otherwise it is 0. This operation is unaffected by context and is+-- quiet – no /flags/ are changed in the context.+isSNaN :: Decimal a b -> Arith p r (Decimal p r)+isSNaN n = return $ case n of+  SNaN{} -> one+  _      -> zero++{- $doctest-isSNaN+>>> op1 Op.isSNaN "2.50"+0++>>> op1 Op.isSNaN "NaN"+0++>>> op1 Op.isSNaN "sNaN"+1+-}++-- | 'isSubnormal' takes one operand. The result is 1 if the operand is a+-- positive or negative /subnormal number/; otherwise it is 0. This operation+-- is quiet; no /flags/ are changed in the context.+isSubnormal :: Precision a => Decimal a b -> Arith p r (Decimal p r)+isSubnormal n = return $ case n of+  _ | Number.isSubnormal n -> one+    | otherwise            -> zero++{- $doctest-isSubnormal+>>> op1 Op.isSubnormal "2.50"+0++>>> op1 Op.isSubnormal "0.1E-999"+1++>>> op1 Op.isSubnormal "0.00"+0++>>> op1 Op.isSubnormal "-Inf"+0++>>> op1 Op.isSubnormal "NaN"+0+-}++-- | 'isZero' takes one operand. The result is 1 if the operand is a zero;+-- otherwise it is 0. This operation is unaffected by context and is quiet –+-- no /flags/ are changed in the context.+isZero :: Decimal a b -> Arith p r (Decimal p r)+isZero n = return $ case n of+  _ | Number.isZero n -> one+    | otherwise       -> zero++{- $doctest-isZero+>>> op1 Op.isZero "0"+1++>>> op1 Op.isZero "2.50"+0++>>> op1 Op.isZero "-0E+2"+1+-}++-- | 'radix' takes no operands. The result is the radix (base) in which+-- arithmetic is effected; for this specification the result will have the+-- value 10.+radix :: Precision p => Arith p r (Decimal p r)+radix = return radix'+  where radix' = case precision radix' of+          Just 1 -> one { exponent    =  1 }+          _      -> one { coefficient = 10 }++{- $doctest-radix+>>> op0 Op.radix+10+-}++-- | 'sameQuantum' takes two operands, and returns 1 if the two operands have+-- the same /exponent/ or 0 otherwise. The result is never affected by either+-- the sign or the coefficient of either operand.+--+-- If either operand is a /special value/, 1 is returned only if both operands+-- are NaNs or both are infinities.+--+-- 'sameQuantum' does not change any /flags/ in the context.+sameQuantum :: Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+sameQuantum Num { exponent = e1 } Num { exponent = e2 }+  | e1 == e2  = return one+  | otherwise = return zero+sameQuantum Inf {} Inf {} = return one+sameQuantum QNaN{} QNaN{} = return one+sameQuantum SNaN{} SNaN{} = return one+sameQuantum QNaN{} SNaN{} = return one+sameQuantum SNaN{} QNaN{} = return one+sameQuantum _      _      = return zero++{- $doctest-sameQuantum+>>> op2 Op.sameQuantum "2.17" "0.001"+0++>>> op2 Op.sameQuantum "2.17" "0.01"+1++>>> op2 Op.sameQuantum "2.17" "0.1"+0++>>> op2 Op.sameQuantum "2.17" "1"+0++>>> op2 Op.sameQuantum "Inf" "-Inf"+1++>>> op2 Op.sameQuantum "NaN" "NaN"+1+-}
src/Numeric/Decimal/Operation.hs-boot view
@@ -8,19 +8,32 @@        , minus        , abs        , compare+       , min+       , max        ) where -import Prelude hiding (abs, compare, subtract)+import Prelude hiding (abs, compare, max, min, subtract) +import {-# SOURCE #-} Numeric.Decimal.Arithmetic import {-# SOURCE #-} Numeric.Decimal.Number import                Numeric.Decimal.Precision-import {-# SOURCE #-} Numeric.Decimal.Rounding+import                Numeric.Decimal.Rounding -add      :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r-subtract :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r-multiply :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r-divide   :: (FinitePrecision p, Rounding r) =>-                                         Number p r -> Number p r -> Number p r-minus    :: (Precision p, Rounding r) => Number p r -> Number p r-abs      :: (Precision p, Rounding r) => Number p r -> Number p r-compare  :: (Precision p, Rounding r) => Number p r -> Number p r -> Number p r+add      :: (Precision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+subtract :: (Precision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+multiply :: (Precision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+divide   :: (FinitePrecision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+minus    :: (Precision p, Rounding r)+         => Decimal a b -> Arith p r (Decimal p r)+abs      :: (Precision p, Rounding r)+         => Decimal a b -> Arith p r (Decimal p r)+compare  :: (Precision p, Rounding r)+         => Decimal a b -> Decimal c d -> Arith p r (Decimal p r)+min      :: (Precision p, Rounding r)+         => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)+max      :: (Precision p, Rounding r)+         => Decimal a b -> Decimal a b -> Arith p r (Decimal a b)
src/Numeric/Decimal/Precision.hs view
@@ -11,18 +11,19 @@         , P75, P100, P150, P200, P250, P300, P400, P500, P1000, P2000 -       , PPlus1, PTimes2+       , PPlus1, PPlus2, PPlus3, PPlus4, PPlus5, PPlus6, PPlus7, PPlus8, PPlus9+       , PTimes2, PTimes10         , PInfinite        ) where --- | Precision indicates the maximum number of significant digits a number may--- have.+-- | Precision indicates the maximum number of significant decimal digits a+-- number may have. class Precision p where   -- | Return the precision of the argument, or 'Nothing' if the precision is infinite.   precision :: p -> Maybe Int --- | A subclass of precisions which are finite+-- | A subclass of precisions that are finite class Precision p => FinitePrecision p  -- | A precision of unlimited significant digits@@ -36,25 +37,43 @@   precision _ = Just 1 instance FinitePrecision P1 --- | A precision of (@p@ + 1) significant digits-data PPlus1 p-instance Precision p => Precision (PPlus1 p) where-  precision pp = (+ 1) <$> precision (minus1 pp)-    where minus1 :: PPlus1 p -> p-          minus1 = undefined-instance FinitePrecision p => FinitePrecision (PPlus1 p)+-- | A precision of (@p@ + @q@) significant digits+data PPlus p q+instance (Precision p, Precision q) => Precision (PPlus p q) where+  precision pq = (+) <$> precision (p pq) <*> precision (q pq)+    where p :: PPlus p q -> p+          p = undefined+          q :: PPlus p q -> q+          q = undefined+instance (FinitePrecision p, FinitePrecision q) => FinitePrecision (PPlus p q) --- | A precision of (@p@ × 2) significant digits-data PTimes2 p-instance Precision p => Precision (PTimes2 p) where-  precision pp = (* 2) <$> precision (div2 pp)-    where div2 :: PTimes2 p -> p-          div2 = undefined-instance FinitePrecision p => FinitePrecision (PTimes2 p)+-- | A precision of (@p@ × @q@) significant digits+data PTimes p q+instance (Precision p, Precision q) => Precision (PTimes p q) where+  precision pq = (*) <$> precision (p pq) <*> precision (q pq)+    where p :: PTimes p q -> p+          p = undefined+          q :: PTimes p q -> q+          q = undefined+instance (FinitePrecision p, FinitePrecision q) => FinitePrecision (PTimes p q) +type PPlus1 p = PPlus p P1  -- ^ A precision of (@p@ + 1) significant digits+type PPlus2 p = PPlus p P2  -- ^ A precision of (@p@ + 2) significant digits+type PPlus3 p = PPlus p P3  -- ^ A precision of (@p@ + 3) significant digits+type PPlus4 p = PPlus p P4  -- ^ A precision of (@p@ + 4) significant digits+type PPlus5 p = PPlus p P5  -- ^ A precision of (@p@ + 5) significant digits+type PPlus6 p = PPlus p P6  -- ^ A precision of (@p@ + 6) significant digits+type PPlus7 p = PPlus p P7  -- ^ A precision of (@p@ + 7) significant digits+type PPlus8 p = PPlus p P8  -- ^ A precision of (@p@ + 8) significant digits+type PPlus9 p = PPlus p P9  -- ^ A precision of (@p@ + 9) significant digits++type PTimes2  = PTimes P2+type PTimes10 = PTimes P10+ -- | A precision of 2 significant digits-type P2  = PTimes2 P1 ; type P3  = PPlus1 P2--- ^ A precision of 3 significant digits+type P2 = PPlus1 P1+-- | A precision of 3 significant digits+type P3 = PPlus1 P2  -- | Et cetera type P4  = PTimes2 P2 ; type P5  = PPlus1 P4@@ -82,15 +101,13 @@ type P48 = PTimes2 P24; type P49 = PPlus1 P48  type P50 = PTimes2 P25-type P62 = PTimes2 P31-type P74 = PTimes2 P37; type P75 = PPlus1 P74+type P75 = PPlus5 (PTimes10 P7)  type P100 = PTimes2 P50-type P124 = PTimes2 P62; type P125 = PPlus1 P124 type P150 = PTimes2 P75  type P200 = PTimes2 P100-type P250 = PTimes2 P125+type P250 = PTimes10 P25  type P300 = PTimes2 P150 type P400 = PTimes2 P200
src/Numeric/Decimal/Rounding.hs view
@@ -1,6 +1,7 @@  module Numeric.Decimal.Rounding-       ( Rounding(..)+       ( RoundingAlgorithm(..)+       , Rounding(..)         , RoundDown        , RoundHalfUp@@ -15,22 +16,32 @@  import Prelude hiding (exponent) -import Numeric.Decimal.Number-import Numeric.Decimal.Precision+import {-# SOURCE #-} Numeric.Decimal.Number+import                Numeric.Decimal.Precision+import {-# SOURCE #-} Numeric.Decimal.Arithmetic +data RoundingAlgorithm = RoundDown+                       | RoundHalfUp+                       | RoundHalfEven+                       | RoundCeiling+                       | RoundFloor+                       | RoundHalfDown+                       | RoundUp+                       | Round05Up+                       deriving (Eq, Enum)+ -- | A rounding algorithm to use when the result of an arithmetic operation -- exceeds the precision of the result type class Rounding r where-  round :: Precision p => Number p r -> Number p r--  isRoundFloor :: Number p r -> Bool-  isRoundFloor _ = False+  rounding :: r -> RoundingAlgorithm+  round :: Precision p => Decimal p r -> Arith p r (Decimal p r)  -- Required...  -- | Round toward 0 (truncate) data RoundDown instance Rounding RoundDown where+  rounding _ = RoundDown   round = roundDown  -- | If the discarded digits represent greater than or equal to half (0.5) of@@ -38,6 +49,7 @@ -- up. If they represent less than half, the value is rounded down. data RoundHalfUp instance Rounding RoundHalfUp where+  rounding _ = RoundHalfUp   round = roundHalfUp  -- | If the discarded digits represent greater than half (0.5) of the value of@@ -46,18 +58,20 @@ -- exactly half, the value is rounded to make its rightmost digit even. data RoundHalfEven instance Rounding RoundHalfEven where+  rounding _ = RoundHalfEven   round = roundHalfEven  -- | Round toward +∞ data RoundCeiling instance Rounding RoundCeiling where+  rounding _ = RoundCeiling   round = roundCeiling  -- | Round toward −∞ data RoundFloor instance Rounding RoundFloor where+  rounding _ = RoundFloor   round = roundFloor-  isRoundFloor _ = True  -- Optional... @@ -66,107 +80,115 @@ -- represent less than half or exactly half, the value is rounded down. data RoundHalfDown instance Rounding RoundHalfDown where+  rounding _ = RoundHalfDown   round = roundHalfDown  -- | Round away from 0 data RoundUp instance Rounding RoundUp where+  rounding _ = RoundUp   round = roundUp  -- | Round zero or five away from 0 data Round05Up instance Rounding Round05Up where+  rounding _ = Round05Up   round = round05Up  -- Implementations +excessDigits :: Precision p => Decimal p r -> Arith p r (Maybe Int)+excessDigits n@Num { coefficient = c } = result+  where result = return (precision n >>= excess)+        d = numDigits c+        excess p+          | d > p     = Just (d - p)+          | otherwise = Nothing+excessDigits _ = return Nothing+ rounded :: (Coefficient -> Coefficient -> Coefficient ->-            Number p r -> Number p r -> Number p r)-        -> Int -> Number p r -> Number p r-rounded f d n = raiseSignal Rounded rounded'+            Decimal p r -> Decimal p r -> Decimal p r)+        -> Int -> Decimal p r -> Arith p r (Decimal p r)+rounded f d n = raiseSignal Rounded =<< rounded' n'   where rounded'-          | r /= 0    = raiseSignal Inexact n'-          | otherwise = n'+          | r /= 0    = raiseSignal Inexact+          | otherwise = return         p = 10 ^ d         (q, r) = coefficient n `quotRem` p         n' = f (p `quot` 2) q r down up-        down = n { coefficient = q-                 , exponent = exponent n + fromIntegral d-                 }-        up = n { coefficient = q + 1-               , exponent = exponent n + fromIntegral d-               }+        down = n { coefficient = q    , exponent = exponent n + fromIntegral d }+        up   = n { coefficient = q + 1, exponent = exponent n + fromIntegral d } -roundDown :: Precision p => Number p r -> Number p r-roundDown n = roundDown' (excessDigits n)-  where roundDown' Nothing  = n+roundDown :: Precision p => Decimal p r -> Arith p r (Decimal p r)+roundDown n = excessDigits n >>= roundDown'+  where roundDown' Nothing  = return n         roundDown' (Just d) = rounded choice d n          choice _h _q _r down _up = down -roundHalfUp :: Precision p => Number p r -> Number p r-roundHalfUp n = roundHalfUp' (excessDigits n)-  where roundHalfUp' Nothing  = n+roundHalfUp :: Precision p => Decimal p r -> Arith p r (Decimal p r)+roundHalfUp n = excessDigits n >>= roundHalfUp'+  where roundHalfUp' Nothing  = return n         roundHalfUp' (Just d) = rounded choice d n          choice h _q r down up-          | r >= h    = roundHalfUp up+          | r >= h    = up           | otherwise = down -roundHalfEven :: Precision p => Number p r -> Number p r-roundHalfEven n = roundHalfEven' (excessDigits n)-  where roundHalfEven' Nothing  = n+roundHalfEven :: Precision p => Decimal p r -> Arith p r (Decimal p r)+roundHalfEven n = excessDigits n >>= roundHalfEven'+  where roundHalfEven' Nothing  = return n         roundHalfEven' (Just d) = rounded choice d n          choice h q r down up = case r `Prelude.compare` h of           LT -> down-          GT -> roundHalfEven up+          GT -> up           EQ | even q    -> down-             | otherwise -> roundHalfEven up+             | otherwise -> up -roundCeiling :: Precision p => Number p r -> Number p r-roundCeiling n = roundCeiling' (excessDigits n)-  where roundCeiling' Nothing  = n+roundCeiling :: Precision p => Decimal p r -> Arith p r (Decimal p r)+roundCeiling n = excessDigits n >>= roundCeiling'+  where roundCeiling' Nothing  = return n         roundCeiling' (Just d) = rounded choice d n          choice _h _q r down up           | r == 0 || sign n == Neg = down-          | otherwise               = roundCeiling up+          | otherwise               = up -roundFloor :: Precision p => Number p r -> Number p r-roundFloor n = roundFloor' (excessDigits n)-  where roundFloor' Nothing  = n+roundFloor :: Precision p => Decimal p r -> Arith p r (Decimal p r)+roundFloor n = excessDigits n >>= roundFloor'+  where roundFloor' Nothing  = return n         roundFloor' (Just d) = rounded choice d n          choice _h _q r down up           | r == 0 || sign n == Pos = down-          | otherwise               = roundFloor up+          | otherwise               = up -roundHalfDown :: Precision p => Number p r -> Number p r-roundHalfDown n = roundHalfDown' (excessDigits n)-  where roundHalfDown' Nothing  = n+roundHalfDown :: Precision p => Decimal p r -> Arith p r (Decimal p r)+roundHalfDown n = excessDigits n >>= roundHalfDown'+  where roundHalfDown' Nothing  = return n         roundHalfDown' (Just d) = rounded choice d n          choice h _q r down up-          | r > h     = roundHalfDown up+          | r > h     = up           | otherwise = down -roundUp :: Precision p => Number p r -> Number p r-roundUp n = roundUp' (excessDigits n)-  where roundUp' Nothing  = n+roundUp :: Precision p => Decimal p r -> Arith p r (Decimal p r)+roundUp n = excessDigits n >>= roundUp'+  where roundUp' Nothing  = return n         roundUp' (Just d) = rounded choice d n          choice _h _q r down up           | r == 0    = down-          | otherwise = roundUp up+          | otherwise = up -round05Up :: Precision p => Number p r -> Number p r-round05Up n = round05Up' (excessDigits n)-  where round05Up' Nothing  = n+round05Up :: Precision p => Decimal p r -> Arith p r (Decimal p r)+round05Up n = excessDigits n >>= round05Up'+  where round05Up' Nothing  = return n         round05Up' (Just d) = rounded choice d n          choice _h q r down up           | r == 0           = down-          | d == 0 || d == 5 = round05Up up  -- overflow -> roundDown?+          | d == 0 || d == 5 = up  -- XXX overflow -> roundDown?           | otherwise        = down           where d = q `rem` 10
− src/Numeric/Decimal/Rounding.hs-boot
@@ -1,14 +0,0 @@--- -*- Haskell -*---module Numeric.Decimal.Rounding-       ( Rounding(..)-       ) where--import {-# SOURCE #-} Numeric.Decimal.Number (Number)-import                Numeric.Decimal.Precision (Precision)--class Rounding r where-  round :: Precision p => Number p r -> Number p r--  isRoundFloor :: Number p r -> Bool-  isRoundFloor _ = False
− test/Spec.hs
@@ -1,30 +0,0 @@--import Numeric.Decimal-import Test.QuickCheck--main :: IO ()-main = putStrLn "Test suite not yet implemented"--infinity :: (Precision p, Rounding r) => Number p r-infinity = read "Infinity"--instance (Precision p, Rounding r) => Arbitrary (Number p r) where-  arbitrary = frequency [(85, genNum), (10, genInf)]--genNum :: (Precision p, Rounding r) => Gen (Number p r)-genNum = do-  c <- choose (-(10^10), 10^10) :: Gen Integer-  e <- choose (-99, 99)         :: Gen Integer-  return $ read (show c ++ 'E' : show e)--genInf :: (Precision p, Rounding r) => Gen (Number p r)-genInf = do-  s <- elements [-1, 1]-  return (s * infinity)--genNaN :: (Precision p, Rounding r) => Gen (Number p r)-genNaN = oneof [nan "", nan "s"]-  where nan kind = do-          s <- elements ["", "-"]-          p <- choose (0, 10000) :: Gen Integer-          return $ read (s ++ kind ++ "NaN" ++ show p)
+ test/doctests.hs view
@@ -0,0 +1,4 @@++import Test.DocTest++main = doctest ["-isrc:test", "src/Numeric/Decimal.hs"]