From 2e80f5b119ae637b8b7733300334f159747b80b2 Mon Sep 17 00:00:00 2001 From: Douglas McClean Date: Wed, 27 Jan 2016 11:52:48 -0500 Subject: [PATCH 1/2] Added AEq instance and convenience wrappers around IEEE functions. Fixes #145. --- CHANGELOG.md | 3 +- dimensional.cabal | 1 + src/Numeric/Units/Dimensional/Float.hs | 82 +++++++++++++++++++++++ src/Numeric/Units/Dimensional/Internal.hs | 4 +- 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 317e1e7e..78329909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,8 @@ vNext unknown dimension, suitable for performing arithmetic with such quantities. * Added `nroot` function for term-level dimensions. * Added the Numeric.Units.Dimensional.Float module with convenient wrappers around functions - from RealFloat for inspecting floating point quantities. + from RealFloat and IEEE for inspecting floating point quantities. +* Added an `AEq` instance for `Quantity`. * Relocated git repository to https://github.com/bjornbm/dimensional 1.0.1.1 (2015-11) diff --git a/dimensional.cabal b/dimensional.cabal index 5c5fc12d..2f27ce9c 100644 --- a/dimensional.cabal +++ b/dimensional.cabal @@ -45,6 +45,7 @@ library build-depends: base >= 4.7 && < 5, deepseq >= 1.3, exact-pi >= 0.2.1.1 && < 0.5, + ieee754 >= 0.7.6, numtype-dk >= 0.5 && < 1.1, vector >= 0.10 hs-source-dirs: src diff --git a/src/Numeric/Units/Dimensional/Float.hs b/src/Numeric/Units/Dimensional/Float.hs index 78d226e1..bd680388 100644 --- a/src/Numeric/Units/Dimensional/Float.hs +++ b/src/Numeric/Units/Dimensional/Float.hs @@ -17,12 +17,25 @@ module Numeric.Units.Dimensional.Float isDenormalized, isInfinite, isNaN, isNegativeZero -- * Convenience Functions , isFiniteNumber, scaleFloat + -- * Lifted Functions from "Numeric.IEEE" + -- ** Values +, infinity, minNormal, maxFinite, epsilon, nan + -- ** Arithmetic +, predIEEE, succIEEE, bisectIEEE, copySign + -- ** NaN with Payload +, nanWithPayload, nanPayload, F.maxNaNPayload + -- ** Comparisons +, identicalIEEE, minNum, maxNum, minNaN, maxNaN ) where import Control.Applicative +import Data.Word (Word64) import Prelude (RealFloat) import qualified Prelude as P +import Numeric.IEEE (IEEE) +import qualified Numeric.IEEE as F +import Numeric.Units.Dimensional.Internal (liftQ, liftQ2) import Numeric.Units.Dimensional.Prelude hiding (RealFloat(..)) import Numeric.Units.Dimensional.Coercion @@ -64,3 +77,72 @@ isNegativeZero = P.isNegativeZero . unQuantity -- True scaleFloat :: RealFloat a => Int -> Quantity d a -> Quantity d a scaleFloat x = Quantity . P.scaleFloat x . unQuantity + +-- | An infinite floating-point quantity. +infinity :: IEEE a => Quantity d a +infinity = Quantity $ F.infinity + +-- | The smallest representable positive quantity whose representation is normalized. +minNormal :: IEEE a => Quantity d a +minNormal = Quantity $ F.minNormal + +-- | The largest representable finite floating-point quantity. +maxFinite :: IEEE a => Quantity d a +maxFinite = Quantity $ F.maxFinite + +-- | The smallest positive value @x@ such that @1 + x@ is representable. +epsilon :: IEEE a => Dimensionless a +epsilon = Quantity $ F.epsilon + +-- | @copySign x y@ returns the quantity @x@ with its sign changed to match that of @y@. +copySign :: IEEE a => Quantity d a -> Quantity d a -> Quantity d a +copySign = liftQ2 F.copySign + +-- | Return 'True' if two floating-point quantities are /exactly/ (bitwise) equal. +identicalIEEE :: IEEE a => Quantity d a -> Quantity d a -> Bool +identicalIEEE (Quantity x) (Quantity y) = F.identicalIEEE x y + +-- | Return the next largest representable floating-point quantity (@Infinity@ and @NaN@ are unchanged). +succIEEE :: IEEE a => Quantity d a -> Quantity d a +succIEEE = liftQ F.succIEEE + +-- | Return the next smallest representable floating-point quantity (@Infinity@ and @NaN@ are unchanged). +predIEEE :: IEEE a => Quantity d a -> Quantity d a +predIEEE = liftQ F.predIEEE + +-- | Given two floating-point quantities with the same sign, return the quantity whose representation is halfway +-- between their representations on the IEEE number line. If the signs of the values differ or either is @NaN@, +-- the value is undefined. +bisectIEEE :: IEEE a => Quantity d a -> Quantity d a -> Quantity d a +bisectIEEE (Quantity x) (Quantity y) = Quantity $ F.bisectIEEE x y + +-- | Default @NaN@ quantity. +nan :: IEEE a => Quantity d a +nan = Quantity $ F.nan + +-- | Quiet @NaN@ quantity with a positive integer payload. +-- Payload must be less than 'maxNaNPayload' of the representation type. +-- +-- Beware that while some platforms allow using 0 as a payload, this behavior is not portable. +nanWithPayload :: IEEE a => Word64 -> Quantity d a +nanWithPayload = Quantity . F.nanWithPayload + +-- | The payload stored in a @NaN@ quantity. Undefined if the argument is not @NaN@. +nanPayload :: IEEE a => Quantity d a -> Word64 +nanPayload = F.nanPayload . unQuantity + +-- | Return the minimum of two quantities; if one value is @NaN@, return the other. Prefer the first if both values are @NaN@. +minNum :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a +minNum = liftQ2 F.minNum + +-- | Return the maximum of two quantities; if one value is @NaN@, return the other. Prefer the first if both values are @NaN@. +maxNum :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a +maxNum = liftQ2 F.maxNum + +-- | Return the minimum of two quantities; if one value is @NaN@, return it. Prefer the first if both values are @NaN@. +minNaN :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a +minNaN = liftQ2 F.minNaN + +-- | Return the maximum of two quantities; if one value is @NaN@, return it. Prefer the first if both values are @NaN@. +maxNaN :: RealFloat a => Quantity d a -> Quantity d a -> Quantity d a +maxNaN = liftQ2 F.maxNaN diff --git a/src/Numeric/Units/Dimensional/Internal.hs b/src/Numeric/Units/Dimensional/Internal.hs index 7dfa4b7a..1f88ed7f 100644 --- a/src/Numeric/Units/Dimensional/Internal.hs +++ b/src/Numeric/Units/Dimensional/Internal.hs @@ -4,6 +4,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} -- for Vector instances only {-# LANGUAGE RankNTypes #-} @@ -27,6 +28,7 @@ where import Control.Applicative import Control.DeepSeq import Control.Monad (liftM) +import Data.AEq (AEq) import Data.Coerce (coerce) import Data.Data import Data.ExactPi @@ -75,7 +77,7 @@ deriving instance Typeable Dimensional instance KnownVariant 'DQuantity where newtype Dimensional 'DQuantity d a = Quantity a - deriving (Eq, Ord, Data, Generic, Generic1 + deriving (Eq, Ord, AEq, Data, Generic, Generic1 #if MIN_VERSION_base(4,8,0) , Typeable -- GHC 7.8 doesn't support deriving this instance #endif From ccf922c44178e61a76401f16dfcaf3bc6d9d306d Mon Sep 17 00:00:00 2001 From: Douglas McClean Date: Fri, 29 Jan 2016 14:29:57 -0500 Subject: [PATCH 2/2] Credit for ieee754 API in module documentation. --- src/Numeric/Units/Dimensional/Float.hs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Numeric/Units/Dimensional/Float.hs b/src/Numeric/Units/Dimensional/Float.hs index bd680388..d2072891 100644 --- a/src/Numeric/Units/Dimensional/Float.hs +++ b/src/Numeric/Units/Dimensional/Float.hs @@ -7,6 +7,11 @@ Defines convenience functions for inspecting and manipulating quantities with 'RealFloat' floating-point representations. + +The dimensionally-typed versions of functions from Patrick Perry's @ieee754@ package +copy that package's API as closely as possible, by permission. In turn they are based on +the @tango@ math library for the D language. + -} {-# LANGUAGE ScopedTypeVariables #-}