fast-math 0.1 → 1.0
raw patch · 6 files changed
+353/−48 lines, 6 filesdep ~basenew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- Numeric/FastMath.hs +15/−22
- Numeric/FastMath/Approximation.hs +256/−0
- Numeric/FastMath/Infinitesimal.hs +0/−11
- Numeric/FastMath/NaN.hs +35/−0
- Numeric/FastMath/SignedZeros.hs +24/−0
- fast-math.cabal +23/−15
Numeric/FastMath.hs view
@@ -1,25 +1,18 @@--- | Compile-time optimisations for 'Float' and 'Double' that break IEEE-754--- compatibility.------ Namely, this otherwise empty module contains RULES that rewrite @x-x@,--- @x*0@ and @0*x@ to @0@, which is incorrect (according to IEEE-754) when--- @x@ is @NaN@.+-- | This module loads all rewrite rules. Unless you know that some rules+-- will be unsafe for your application, this is the module you should load.+-- Importing this module is roughly equivalent to gcc's @-ffast-math@ +-- compilation flag. ----- At the time of writing, @base-4.3.1.0:GHC/Base.lhs@ erroneously includes--- these rules for 'Float's, but not for 'Double's. This has been reported--- as GHC bug #5178: <http://hackage.haskell.org/trac/ghc/ticket/5178>.--module Numeric.FastMath () where--import GHC.Exts--{-# RULES-"minusFloat x x" forall x#. minusFloat# x# x# = 0.0#-"timesFloat x 0.0" forall x#. timesFloat# x# 0.0# = 0.0#-"timesFloat 0.0 x" forall x#. timesFloat# 0.0# x# = 0.0#+-- The best way to figure out what optimizations these modules do is by +-- looking at the source code. RULES pragmas are surprisingly readable. -"minusDouble x x" forall x#. (-##) x# x# = 0.0##-"timesDouble 0.0 x" forall x#. (*##) 0.0## x# = 0.0##-"timesDouble x 0.0" forall x#. (*##) x# 0.0## = 0.0##- #-}+module Numeric.FastMath+ ( module Numeric.FastMath.Approximation+ , module Numeric.FastMath.NaN+ , module Numeric.FastMath.SignedZeros+ )+ where +import Numeric.FastMath.Approximation ()+import Numeric.FastMath.NaN ()+import Numeric.FastMath.SignedZeros ()
+ Numeric/FastMath/Approximation.hs view
@@ -0,0 +1,256 @@+-- | This module contains rewrite rules that may change the lowest order bits+-- of a computation. They take advantage of:+--+-- * distributivity+--+-- * repeated addition/multiplication+--+-- * exponentiation rules +--+-- All of these RULES should be safe in the presence of `NaN` and `Infinity`+--+-- Importing this module is similar to compiling with gcc's+-- @-funsafe-math-operations@.+--+module Numeric.FastMath.Approximation+ where++import GHC.Exts+import Prelude++---------------------------------------+-- distributivity+--+-- NOTE: these rules are sufficient to capture the property+--+-- > x*y1+x*y2+x*y3 == x*(y1+y2+y3)+--+-- because they will be applied recursively during the optimization passes++{-# RULES++"double *,+ distribute A" forall x y1 y2. (x *## y1) +## (x *## y2) + = x *## (y1 +## y2)++"double *,+ distribute B" forall x y1 y2. (y1 *## x) +## (x *## y2) + = x *## (y1 +## y2)++"double *,+ distribute C" forall x y1 y2. (y1 *## x) +## (y2 *## x) + = x *## (y1 +## y2)++"double *,+ distribute D" forall x y1 y2. (x *## y1) +## (y2 *## x) + = x *## (y1 +## y2)++++"double *,- distribute A" forall x y1 y2. (x *## y1) -## (x *## y2) + = x *## (y1 -## y2)++"double *,- distribute B" forall x y1 y2. (y1 *## x) -## (x *## y2) + = x *## (y1 -## y2)++"double *,- distribute C" forall x y1 y2. (y1 *## x) -## (y2 *## x) + = x *## (y1 -## y2)++"double *,- distribute D" forall x y1 y2. (x *## y1) -## (y2 *## x) + = x *## (y1 -## y2)++++"double /,+ distribute" forall x y1 y2. (y1 *## x) +## (y2 *## x) + = (y1 +## y2) /## x++"double /,- distribute" forall x y1 y2. (y1 /## x) -## (y2 /## x) + = (y1 -## y2) /## x++ #-}++++{-# RULES++"float *,+ distribute A" forall x y1 y2. (x `timesFloat#` y1) `plusFloat#` (x `timesFloat#` y2) + = x `timesFloat#` (y1 `plusFloat#` y2)++"float *,+ distribute B" forall x y1 y2. (y1 `timesFloat#` x) `plusFloat#` (x `timesFloat#` y2) + = x `timesFloat#` (y1 `plusFloat#` y2)++"float *,+ distribute C" forall x y1 y2. (y1 `timesFloat#` x) `plusFloat#` (y2 `timesFloat#` x) + = x `timesFloat#` (y1 `plusFloat#` y2)++"float *,+ distribute D" forall x y1 y2. (x `timesFloat#` y1) `plusFloat#` (y2 `timesFloat#` x) + = x `timesFloat#` (y1 `plusFloat#` y2)++++"float *,- distribute A" forall x y1 y2. (x `timesFloat#` y1) `minusFloat#` (x `timesFloat#` y2) + = x `timesFloat#` (y1 `minusFloat#` y2)++"float *,- distribute B" forall x y1 y2. (y1 `timesFloat#` x) `minusFloat#` (x `timesFloat#` y2) + = x `timesFloat#` (y1 `minusFloat#` y2)++"float *,- distribute C" forall x y1 y2. (y1 `timesFloat#` x) `minusFloat#` (y2 `timesFloat#` x) + = x `timesFloat#` (y1 `minusFloat#` y2)++"float *,- distribute D" forall x y1 y2. (x `timesFloat#` y1) `minusFloat#` (y2 `timesFloat#` x) + = x `timesFloat#` (y1 `minusFloat#` y2)++++"float /,+ distribute" forall x y1 y2. (y1 `timesFloat#` x) `plusFloat#` (y2 `timesFloat#` x) + = (y1 `plusFloat#` y2) `divideFloat#` x++"float /,- distribute" forall x y1 y2. (y1 `divideFloat#` x) `minusFloat#` (y2 `divideFloat#` x) + = (y1 `minusFloat#` y2) `divideFloat#` x++ #-}++---------------------------------------+-- fancy distributing+--+-- NOTE: I'm not yet sure if all of these are a great idea to have on by +-- default due to stability issues...++{-# RULES++"double **,* distribute" forall x y1 y2. (y1 **## x) *## (y2 **## x) = (y1 *## y2) *## x++"double **,log distribute" forall x y. logDouble# (x **## y) = y *## (logDouble# x)++ #-}++---------------------------------------+-- Repeated addition+--+-- NOTE: It is important that these rules should fire after the distributivity+-- rules. This ensures that+--+-- > x*x+x*y+--+-- gets simplified to+--+-- > x*(x+y)+--+-- rather than +--+-- > x+x+x*y+--+{-# RULES ++"double mulToAdd 2" [0] forall x . x *## 2.0## = x +## x+"double mulToAdd 3" [0] forall x . x *## 3.0## = x +## x +## x+"double mulToAdd 4" [0] forall x . x *## 4.0## = x +## x +## x +## x++ #-}++{-# RULES++"float mulToAdd 2" [0] forall x . timesFloat# x 2.0# = plusFloat# x x+"float mulToAdd 3" [0] forall x . timesFloat# x 3.0# = plusFloat# x (plusFloat# x x)+"float mulToAdd 4" [0] forall x . timesFloat# x 4.0# = plusFloat# x (plusFloat# x (plusFloat# x x))++ #-}++---------------------------------------+-- left associate / commute++-- NOTE: phase controls are needed to prevent infinite loops when interacting +-- with the repeated multiplication rules.+--+-- We should slightly prefer commuting rather than associating because it doesn't +-- change the floating point results++{-# RULES++"double commute left *" [~2] forall x1 x2 x3. (*##) x1 ((*##) x2 x3) = (*##) ((*##) x2 x3) x1+"double associate left *" [~1] forall x1 x2 x3. (*##) x1 ((*##) x2 x3) = (*##) ((*##) x1 x2) x3++"double commute left +" [~2] forall x1 x2 x3. (+##) x1 ((+##) x2 x3) = (+##) ((+##) x2 x3) x1+"double associate left +" [~1] forall x1 x2 x3. (+##) x1 ((+##) x2 x3) = (+##) ((+##) x1 x2) x3++ #-}++{-# RULES++"float commute left *" [~2] forall x1 x2 x3. timesFloat# x1 (timesFloat# x2 x3) = timesFloat# (timesFloat# x2 x3) x1+"float associate left *" [~1] forall x1 x2 x3. timesFloat# x1 (timesFloat# x2 x3) = timesFloat# (timesFloat# x1 x2) x3++"float commute left +" [~2] forall x1 x2 x3. plusFloat# x1 (plusFloat# x2 x3) = plusFloat# (plusFloat# x2 x3) x1+"float associate left +" [~1] forall x1 x2 x3. plusFloat# x1 (plusFloat# x2 x3) = plusFloat# (plusFloat# x1 x2) x3++ #-}++---------------------------------------+-- Repeated multiplication++-- FIXME: I can't get thise rules to work for more than 4 repeats without+-- causing an infinite loop in the simplifier++{-# RULES++"double repmul 4" [1] forall x . ((x *## x) *## x) *## x + = let xx = (x *## x) in (xx *## xx)++ #-}++-- "double repmul 5" forall x . x *## x *## x *## x *## x +-- = let xx = x *## x in xx *## xx *## x+-- +-- "double repmul 6" forall x . x *## x *## x *## x *## x *## x+-- = let xx = x *## x in xx *## xx *## xx+-- +-- "double repmul 7" forall x . x *## x *## x *## x *## x *## x *## x+-- = let xx = x *## x in xx *## xx *## xx *## x+-- +-- "double repmul 8" forall x . x *## x *## x *## x *## x *## x *## x *## x +-- = let xxx = (let xx = x *## x in xx *## xx) in xxx *## xxx++{-# RULES++"double repmul 4" forall x . timesFloat# x (timesFloat# x (timesFloat# x x))+ = let xx = timesFloat# x x in timesFloat# xx xx++ #-}++-- "double repmul 5" forall x . timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x x)))+-- = let xx = timesFloat# x x in timesFloat# x (timesFloat# xx xx)+-- +-- "double repmul 6" forall x . timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x x))))+-- = let xx = timesFloat# x x in timesFloat# xx (timesFloat# xx xx)+-- +-- "double repmul 7" forall x . timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x x)))))+-- = let xx = timesFloat# x x in timesFloat# x (timesFloat# xx (timesFloat# xx xx))+-- +-- "double repmul 8" forall x . timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x (timesFloat# x x))))))+-- = let xxx = (let xx = timesFloat# x x in timesFloat# xx xx) in timesFloat# xxx xxx+++---------------------------------------+-- Exponentiation ++{-# RULES +"double **0" forall x . x **## 0.0## = 1.0##+"double **1" forall x . x **## 1.0## = x+"double **2" forall x . x **## 2.0## = x *## x+"double **3" forall x . x **## 3.0## = x *## x *## x+"double **4" forall x . x **## 4.0## = let xx = x *## x in xx *## xx+"double **8" forall x . x **## 8.0## = let xxx = (let xx = x *## x in xx *## xx) in xxx *## xxx++"double **(1/2)" forall x## . x## **## 0.500## = sqrtDouble# x##+"double **(1/4)" forall x## . x## **## 0.250## = sqrtDouble# (sqrtDouble# x##)+"double **(1/8)" forall x## . x## **## 0.125## = sqrtDouble# (sqrtDouble# (sqrtDouble# x##))+ #-}++{-# RULES+"float **0" forall x# . powerFloat# x# 0.0# = 1.0#+"float **1" forall x# . powerFloat# x# 1.0# = x#+"float **2" forall x# . powerFloat# x# 2.0# = timesFloat# x# x#+"float **3" forall x# . powerFloat# x# 3.0# = timesFloat# (timesFloat# x# x#) x#+"float **4" forall x# . powerFloat# x# 4.0# = let xx# = (timesFloat# x# x#) in timesFloat# xx# xx#+"float **8" forall x# . powerFloat# x# 8.0# = let xxx# = (let xx# = (timesFloat# x# x#) in timesFloat# xx# xx#) in timesFloat# xxx# xxx#++"float **(1/2)" forall x# . powerFloat# x# 0.500# = sqrtFloat# x#+"float **(1/4)" forall x# . powerFloat# x# 0.250# = sqrtFloat# (sqrtFloat# x#)+"float **(1/8)" forall x# . powerFloat# x# 0.125# = sqrtFloat# (sqrtFloat# (sqrtFloat# x#))+ #-}+
− Numeric/FastMath/Infinitesimal.hs
@@ -1,11 +0,0 @@--- | Also rewrite @0/x@ to @+0@, which should really be @-0@ for negative @x@.--module Numeric.FastMath.Infinitesimal () where--import GHC.Exts--{-# RULES-"divideFloat 0.0 x" forall x#. divideFloat# 0.0# x# = 0.0#-"divideDouble 0.0 x" forall x#. (/##) 0.0## x# = 0.0##- #-}-
+ Numeric/FastMath/NaN.hs view
@@ -0,0 +1,35 @@+-- | This module contains rules that break the way NaN is handled for "Float" +-- and "Double" types. Still, these rules should be safe in the vast majority of+-- applications.+--+-- Importing this module is similar to compiling with gcc's @-fno-signaling-nans@+-- and @-ffinite-math-only@.+--+module Numeric.FastMath.NaN+ where++import GHC.Exts++{-# RULES+"minusDouble x x" forall x. (-##) x x = 0.0##++"timesDouble 0 x" forall x. (*##) 0.0## x = 0.0##+"timesDouble x 0" forall x. (*##) x 0.0## = 0.0##++"divideDouble x 1" forall x. (/##) x 1.0## = x+"divideDouble x -1" forall x. (/##) x (-1.0##) = negateDouble# x+"divideDouble 0 x" forall x. (/##) 0.0## x = 0.0##+ #-}++{-# RULES+"minusFloat x x" forall x. minusFloat# x x = 0.0#++"timesFloat x 0" forall x. timesFloat# x 0.0# = 0.0#+"timesFloat 0 x" forall x. timesFloat# 0.0# x = 0.0#++"divideFloat x 1" forall x. divideFloat# x 1.0# = x+"divideFloat x -1" forall x. divideFloat# x (-1.0#) = negateFloat# x+"divideFloat 0 x" forall x. divideFloat# 0.0# x = 0.0#++ #-}+
+ Numeric/FastMath/SignedZeros.hs view
@@ -0,0 +1,24 @@+-- | IEEE 754 math makes a distrinction between -0.0 and +0.0. This module+-- contains RULES that ignore this distinction. +--+-- Importing this module is similar to compiling with gcc's+-- @-fno-signed-zeros@.++module Numeric.FastMath.SignedZeros () where++import GHC.Exts++{-# RULES++"minusDouble 0 x" forall x. (-##) 0.0## x = negateDouble# x+"divideDouble 0 x" forall x. (/##) 0.0## x = 0.0##+ #-}++{-# RULES++"minusFloat 0 x" forall x. minusFloat# 0.0# x = negateFloat# x+"divideFloat 0 x" forall x. divideFloat# 0.0# x = 0.0#++ #-}++
fast-math.cabal view
@@ -1,34 +1,42 @@ name: fast-math-version: 0.1+version: 1.0 synopsis: Non IEEE-754 compliant compile-time floating-point optimisations description:- The "Numeric.FastMath" module brings into scope RULES for 'Float's and- 'Double's that rewrite @x-x@, @0*x@ and @x*0@ to @0@. This disagrees- with IEEE-754 when @x@ is @NaN@, but is acceptable for most- applications.- .- Importing "Numeric.FastMath.Infinitesimal" also rewrites @0/x@ to @0@.+ The "Numeric.FastMath" module brings into scope many unsafe @RULES@ for + 'Float's and 'Double's that can greatly improve run time performance.+ It is roughly equivalent to gcc\'s @-ffast-math@ compiler flag.+ Optimisation (at least @-O1@) must be enabled for any @RULES@ to take effect. .- Optimisation (at least @-O1@) must be enabled for any RULES to take effect.+ These rules are unsafe because they don't strictly adhere to the+ IEEE-754 regulations and may subtly change the results of your numeric computations.+ See the <http://github.com/liyang/fast-math/ README> on github for more details.+ license: BSD3 license-file: LICENSE-author: Liyang HU+author: Liyang HU and Mike Izbicki maintainer: fast-math@liyang.hu copyright: © 2011, Liyang HU category: Math, Numeric build-type: Simple-cabal-version: >= 1.2.3+cabal-version: >= 1.10 +source-repository head+ type: git+ location: http://github.com/liyang/fast-math+ library+ default-language: Haskell2010 build-depends: base >= 3 && < 5 exposed-modules: Numeric.FastMath- Numeric.FastMath.Infinitesimal- extensions:+ Numeric.FastMath.Approximation+ Numeric.FastMath.NaN+ Numeric.FastMath.SignedZeros+ default-extensions: NoImplicitPrelude MagicHash- ghc-options: -Wall -fno-warn-orphans---- vim: et ts=4 sw=4:+ ghc-options: + -Wall + -fno-warn-orphans