packages feed

Mantissa (empty) → 0.1.0.0

raw patch · 9 files changed

+399/−0 lines, 9 filesdep +Mantissadep +basedep +hspecsetup-changed

Dependencies added: Mantissa, base, hspec

Files

+ CHANGELOG.md view
@@ -0,0 +1,18 @@+# Changelog for `Mantissa`++All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),+and this project adheres to the+[Haskell Package Versioning Policy](https://pvp.haskell.org/).++## Unreleased++## 0.1.0.0 - 2024-09-04++Initial release.++- Boxed and unboxed versions+- Basic arithmetic+- Basic conversions+- Num, Fractional, Eq and Ord instances
+ LICENSE view
@@ -0,0 +1,26 @@+Copyright 2024 James Cranch++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1.  Redistributions of source code must retain the above copyright notice, this+    list of conditions and the following disclaimer.++2.  Redistributions in binary form must reproduce the above copyright notice,+    this list of conditions and the following disclaimer in the documentation+    and/or other materials provided with the distribution.++3.  Neither the name of the copyright holder nor the names of its contributors+    may be used to endorse or promote products derived from this software+    without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Mantissa.cabal view
@@ -0,0 +1,58 @@+cabal-version: 2.2++-- This file has been generated from package.yaml by hpack version 0.36.0.+--+-- see: https://github.com/sol/hpack++name:           Mantissa+version:        0.1.0.0+synopsis:       Reals in the interval [0,1), as machine words+description:    Please see the README on GitHub at <https://github.com/jcranch/Mantissa#readme>+category:       Numeric+homepage:       https://github.com/jcranch/mantissa#readme+bug-reports:    https://github.com/jcranch/mantissa/issues+author:         James Cranch+maintainer:     cranch@cantab.net+copyright:      2024 James Cranch+license:        BSD-3-Clause+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    CHANGELOG.md++source-repository head+  type: git+  location: https://github.com/jcranch/mantissa++library+  exposed-modules:+      Numeric.Mantissa+      Numeric.Mantissa.Unboxed+  other-modules:+      Paths_Mantissa+  autogen-modules:+      Paths_Mantissa+  hs-source-dirs:+      src+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints+  build-depends:+      base >=4.7 && <5+  default-language: Haskell2010++test-suite Mantissa-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Numeric.MantissaSpec+      Paths_Mantissa+  autogen-modules:+      Paths_Mantissa+  hs-source-dirs:+      test+  ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -Wwarn=missing-home-modules -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      Mantissa+    , base >=4.7 && <5+    , hspec >=2.7 && <2.12+  default-language: Haskell2010
+ README.md view
@@ -0,0 +1,21 @@+# Mantissa++Reals in the interval [0,1), implemented as machine words. Boxed and+unboxed variants are available.++Arithmetic is available; addition and subtraction is taken+modulo 1. On the whole, the user is responsible for ensuring that,+when these numbers are produced by division (in any of several+possible ways), the result will be in the interval [0,1).++## Nomenclature++As of 2024, most people would call this concept a+[fractional part](https://en.wikipedia.org/wiki/Fractional_part),+but the name+[Fractional](https://hackage.haskell.org/package/base-4.20.0.1/docs/Prelude.html#t:Fractional)+is already in use in Haskell base for something different. Thus we use the slightly obscure name+[mantissa](https://en.wikipedia.org/wiki/Common_logarithm#Mantissa_and_characteristic).+++
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Numeric/Mantissa.hs view
@@ -0,0 +1,104 @@+{-#+    LANGUAGE+      BangPatterns,+      MagicHash,+      UnboxedTuples,+      UnliftedNewtypes+  #-}++-- | Real numbers in [0,1), represented as fixed-point reals stored in+-- a machine word.+--+-- `Fractional` would arguably be a better name, but is of course+-- already in use.+module Numeric.Mantissa (+  Mantissa(..),+  oneMinusE,+  mantissaFromWord,+  mantissaToWord,+  mantissaToFractional,+  ) where++import Data.Ratio ((%), numerator, denominator)+import GHC.Num (integerToWord, integerFromWord)+import GHC.Exts (Word(..), isTrue#, not#)+import Numeric.Mantissa.Unboxed+++-- | A real number in [0,1), represented as a boxed word+data Mantissa = M# Mantissa#++-- | The largest possible mantissa, just a tiny bit less than one+oneMinusE :: Mantissa+oneMinusE = M# (Mantissa# (not# 0##))++instance Num Mantissa where++  -- | Addition is modulo 1+  M# a + M# b = M# (plusMod1Mantissa# a b)+  +  -- | Subtraction is modulo 1+  M# a - M# b = M# (minusMod1Mantissa# a b)++  M# a * M# b = M# (timesMantissa# a b)++  -- | All mantissas are positive, so `abs` is the identity function+  abs = id++  -- | The only integer representable as a mantissa is 0+  fromInteger 0 = M# (Mantissa# 0##)+  fromInteger _ = error "fromInteger: can only convert 0 to mantissa"++  -- | Note that `signum` does not return 1 for positive numbers,+  -- since 1 is not available+  signum 0 = 0+  signum _ = oneMinusE++instance Eq Mantissa where+  M# a == M# b = isTrue# (eqMantissa# a b)++instance Ord Mantissa where+  M# a < M# b = isTrue# (ltMantissa# a b)+  M# a > M# b = isTrue# (gtMantissa# a b)+  M# a <= M# b = isTrue# (leMantissa# a b)+  M# a >= M# b = isTrue# (geMantissa# a b)++mantissaFromWord :: Word -> Mantissa+mantissaFromWord (W# w) = M# (Mantissa# w)++mantissaToWord :: Mantissa -> Word+mantissaToWord (M# (Mantissa# w)) = W# w++-- | The unsigned integer stored in a mantissa ("what multiple of the+-- smallest possible mantissa is this?"). This is used as a step in+-- some more mathematically meaningful functionality.+mantissaSerial :: Mantissa -> Integer+mantissaSerial = integerFromWord . mantissaToWord++-- | One more than the largest integer in a word (2^64 on a 64-bit+-- machine, for example).+--+-- I'd be interested to know if there's a more idiomatic way of doing+-- this.+wordMultiple :: Integer+wordMultiple = 1 + mantissaSerial oneMinusE++instance Fractional Mantissa where++  -- | The user is responsible for ensuring that, when they take a/b,+  -- then a is less than b.+  M# a / M# b = M# (quotMantissa# a b)++  recip _ = error "recip: cannot take reciprocal of a mantissa"++  -- | The user is responsible for ensuring that, when they use+  -- fromRational on a fraction, the numerator is less than the+  -- denominator (and both are positive).+  fromRational u = let+    q = quot (wordMultiple * numerator u) (denominator u)+    in mantissaFromWord (integerToWord q)++-- | Convert a mantissa to any Fractional type, implemented using+-- `fromRational`+mantissaToFractional :: Fractional a => Mantissa -> a+mantissaToFractional m = fromRational (mantissaSerial m % wordMultiple)
+ src/Numeric/Mantissa/Unboxed.hs view
@@ -0,0 +1,84 @@+{-#+    LANGUAGE+      BangPatterns,+      MagicHash,+      UnboxedTuples,+      UnliftedNewtypes+  #-}++-- | Real numbers in [0,1), represented as fixed-point reals stored in+-- a machine word.+--+-- `Fractional` would arguably be a better name, but is of course+-- already in use.+module Numeric.Mantissa.Unboxed (+  Mantissa#(..),+  eqMantissa#,+  neMantissa#,+  gtMantissa#,+  ltMantissa#,+  geMantissa#,+  leMantissa#,+  plusMod1Mantissa#,+  minusMod1Mantissa#,+  plusWithOverflowMantissa#,+  minusWithOverflowMantissa#,+  timesMantissa#,+  quotMantissa#,+  ) where++import GHC.Exts+++-- | A real number in [0,1), represented as an unboxed word+newtype Mantissa# = Mantissa# Word#++eqMantissa# :: Mantissa# -> Mantissa# -> Int#+eqMantissa# (Mantissa# a) (Mantissa# b) = eqWord# a b++neMantissa# :: Mantissa# -> Mantissa# -> Int#+neMantissa# (Mantissa# a) (Mantissa# b) = neWord# a b++ltMantissa# :: Mantissa# -> Mantissa# -> Int#+ltMantissa# (Mantissa# a) (Mantissa# b) = ltWord# a b++gtMantissa# :: Mantissa# -> Mantissa# -> Int#+gtMantissa# (Mantissa# a) (Mantissa# b) = gtWord# a b++leMantissa# :: Mantissa# -> Mantissa# -> Int#+leMantissa# (Mantissa# a) (Mantissa# b) = leWord# a b++geMantissa# :: Mantissa# -> Mantissa# -> Int#+geMantissa# (Mantissa# a) (Mantissa# b) = geWord# a b++-- | Addition modulo 1+plusMod1Mantissa# :: Mantissa# -> Mantissa# -> Mantissa#+plusMod1Mantissa# (Mantissa# a) (Mantissa# b) = Mantissa# (plusWord# a b)++-- | Subtraction modulo 1+minusMod1Mantissa# :: Mantissa# -> Mantissa# -> Mantissa#+minusMod1Mantissa# (Mantissa# a) (Mantissa# b) = Mantissa# (minusWord# a b)++-- | The second Int# component is nonzero on overflow+plusWithOverflowMantissa# :: Mantissa# -> Mantissa# -> (# Mantissa#, Int# #)+plusWithOverflowMantissa# (Mantissa# a) (Mantissa# b) = let+  !(# c, i #) = addWordC# a b+  in (# Mantissa# c, i #)++-- | The second Int# component is nonzero on overflow+minusWithOverflowMantissa# :: Mantissa# -> Mantissa# -> (# Mantissa#, Int# #)+minusWithOverflowMantissa# (Mantissa# a) (Mantissa# b) = let+  !(# c, i #) = subWordC# a b+  in (# Mantissa# c, i #)++timesMantissa# :: Mantissa# -> Mantissa# -> Mantissa#+timesMantissa# (Mantissa# a) (Mantissa# b) = let+  !(# c, _ #) = timesWord2# a b+  in Mantissa# c++-- | The user is responsible for ensuring that, when they take+-- dividend/divisor, then the dividend is less than the divisor.+quotMantissa# :: Mantissa# -> Mantissa# -> Mantissa#+quotMantissa# (Mantissa# a) (Mantissa# b) = let+  !(# c, _ #) = quotRemWord2# a 0## b+  in Mantissa# c
+ test/Numeric/MantissaSpec.hs view
@@ -0,0 +1,84 @@+module Numeric.MantissaSpec (spec) where++import Data.Ratio ((%))+import Test.Hspec+import Numeric.Mantissa+++spec :: Spec+spec = do+++  describe "Comparisons" $ do++    let twoSevenths = fromRational (2%7) :: Mantissa+    let sixSevenths = fromRational (6%7) :: Mantissa+    +    it "test == (when true)" $ do+      twoSevenths == twoSevenths `shouldBe` True++    it "test == (when false)" $ do+      twoSevenths == sixSevenths `shouldBe` False++    it "test a <= b (when a < b)" $ do+      twoSevenths <= sixSevenths `shouldBe` True++    it "test a <= b (when a = b)" $ do+      twoSevenths <= twoSevenths `shouldBe` True++    it "test a <= b (when a > b)" $ do+      sixSevenths <= twoSevenths `shouldBe` False++    it "test a >= b (when a < b)" $ do+      twoSevenths >= sixSevenths `shouldBe` False++    it "test a >= b (when a = b)" $ do+      twoSevenths >= twoSevenths `shouldBe` True++    it "test a >= b (when a > b)" $ do+      sixSevenths >= twoSevenths `shouldBe` True++    it "test a < b (when a < b)" $ do+      twoSevenths < sixSevenths `shouldBe` True++    it "test a < b (when a = b)" $ do+      twoSevenths < twoSevenths `shouldBe` False++    it "test a < b (when a > b)" $ do+      sixSevenths < twoSevenths `shouldBe` False++    it "test a > b (when a < b)" $ do+      twoSevenths > sixSevenths `shouldBe` False++    it "test a > b (when a = b)" $ do+      twoSevenths > twoSevenths `shouldBe` False++    it "test a > b (when a > b)" $ do+      sixSevenths > twoSevenths `shouldBe` True++  +  describe "Basic arithmetic" $ do++    it "addition" $ do+      mantissaToFractional (fromRational (1%3) + fromRational (1%5)) `shouldBe`+        (fromRational (8%15) :: Float)+      +    it "subtraction" $ do+      mantissaToFractional (fromRational (1%3) - fromRational (1%5)) `shouldBe`+        (fromRational (2%15) :: Float)+      +    it "addition (with wraparound)" $ do+      mantissaToFractional (fromRational (2%3) + fromRational (4%5)) `shouldBe`+        (fromRational (7%15) :: Float)+      +    it "subtraction (with wraparound)" $ do+      mantissaToFractional (fromRational (1%5) - fromRational (1%3)) `shouldBe`+        (fromRational (13%15) :: Float)+      +    it "multiplication" $ do+      mantissaToFractional (fromRational (1%3) * fromRational (1%5)) `shouldBe`+        (fromRational (1%15) :: Float)+      +    it "division" $ do+      mantissaToFractional (fromRational (1%5) / fromRational (1%3)) `shouldBe`+        (fromRational (3%5) :: Float)
+ test/Spec.hs view
@@ -0,0 +1,2 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}+-- Autodiscover spec files