scientific 0.3.6.2 → 0.3.7.0
raw patch · 9 files changed
+258/−126 lines, 9 filesdep +invalid-cabal-flag-settingsdep +template-haskelldep −tasty-ant-xmldep ~QuickCheckdep ~basedep ~binarynew-uploader
Dependencies added: invalid-cabal-flag-settings, template-haskell
Dependencies removed: tasty-ant-xml
Dependency ranges changed: QuickCheck, base, binary, bytestring, bytestring-builder, containers, deepseq, hashable, integer-logarithms, primitive, tasty, text
Files
- bench/bench.hs +15/−0
- changelog +11/−0
- scientific.cabal +97/−63
- src/Data/ByteString/Builder/Scientific.hs +1/−1
- src/Data/Scientific.hs +62/−58
- src/Data/Text/Lazy/Builder/Scientific.hs +1/−1
- src/GHC/Integer/Compat.hs +9/−1
- src/Utils.hs +55/−0
- test/test.hs +7/−2
bench/bench.hs view
@@ -57,6 +57,21 @@ , bgroup "dangerouslyBig" $ benchToBoundedInteger dangerouslyBig , bgroup "64" $ benchToBoundedInteger 64 ]++ , bgroup "read"+ [ benchRead "123456789.123456789"+ , benchRead "12345678900000000000.12345678900000000000000000"+ , benchRead "12345678900000000000.12345678900000000000000000e1234"+ ]++ , bgroup "division"+ [ bench (show n ++ " / " ++ show d) $ nf (uncurry (/)) t+ | t@(n, d) <-+ [ (0.4 , 20.0)+ , (0.4e-100, 0.2e50)+ ] :: [(Scientific, Scientific)]+ ]+ ] where pos :: Fractional a => a
changelog view
@@ -1,3 +1,14 @@+0.3.7.0++ * Make division (/) on Scientifics slightly more efficient.++ * Fix the Show instance to surround negative numbers with parentheses when+ necessary.++ * Add (Template Haskell) Lift Scientific instance++ * Mark modules as Safe or Trustworthy (Safe Haskell).+ 0.3.6.2 * Due to a regression introduced in 0.3.4.14 the RealFrac methods and floatingOrInteger became vulnerable to a space blowup when
scientific.cabal view
@@ -1,6 +1,6 @@-name: scientific-version: 0.3.6.2-synopsis: Numbers represented using scientific notation+name: scientific+version: 0.3.7.0+synopsis: Numbers represented using scientific notation description: "Data.Scientific" provides the number type 'Scientific'. Scientific numbers are arbitrary precision and space efficient. They are represented using@@ -32,32 +32,36 @@ @'Integral's@ (like: 'Int') or @'RealFloat's@ (like: 'Double' or 'Float') will always be bounded by the target type. -homepage: https://github.com/basvandijk/scientific-bug-reports: https://github.com/basvandijk/scientific/issues-license: BSD3-license-file: LICENSE-author: Bas van Dijk-maintainer: Bas van Dijk <v.dijk.bas@gmail.com>-category: Data-build-type: Simple-cabal-version: >=1.10--extra-source-files:- changelog--Tested-With: GHC == 7.6.3- , GHC == 7.8.4- , GHC == 7.10.3- , GHC == 8.0.2- , GHC == 8.2.2- , GHC == 8.4.1+homepage: https://github.com/basvandijk/scientific+bug-reports: https://github.com/basvandijk/scientific/issues+license: BSD3+license-file: LICENSE+author: Bas van Dijk+maintainer: Bas van Dijk <v.dijk.bas@gmail.com>+category: Data+build-type: Simple+cabal-version: >=1.10+extra-source-files: changelog+tested-with:+ GHC ==7.6.3+ || ==7.8.4+ || ==7.10.3+ || ==8.0.2+ || ==8.2.2+ || ==8.4.4+ || ==8.6.5+ || ==8.8.4+ || ==8.10.4+ || ==9.0.1 source-repository head type: git location: git://github.com/basvandijk/scientific.git flag bytestring-builder- description: Depend on the bytestring-builder package for backwards compatibility.+ description:+ Depend on the bytestring-builder package for backwards compatibility.+ default: False manual: False @@ -66,60 +70,89 @@ default: False library- exposed-modules: Data.ByteString.Builder.Scientific- Data.Scientific- Data.Text.Lazy.Builder.Scientific- other-modules: GHC.Integer.Compat- Utils- other-extensions: DeriveDataTypeable, BangPatterns- ghc-options: -Wall- build-depends: base >= 4.3 && < 5- , integer-logarithms >= 1- , deepseq >= 1.3- , text >= 0.8- , hashable >= 1.1.2- , primitive >= 0.1- , containers >= 0.1- , binary >= 0.4.1+ exposed-modules:+ Data.ByteString.Builder.Scientific+ Data.Scientific+ Data.Text.Lazy.Builder.Scientific + other-modules:+ GHC.Integer.Compat+ Utils++ other-extensions:+ BangPatterns+ DeriveDataTypeable+ Trustworthy++ ghc-options: -Wall+ build-depends:+ base >=4.5 && <4.16+ , binary >=0.5.1 && <0.9+ , containers >=0.4.2.1 && <0.7+ , deepseq >=1.3.0.0 && <1.5+ , hashable >=1.2.7.0 && <1.4+ , integer-logarithms >=1.0.3.1 && <1.1+ , primitive >=0.7.1.0 && <0.8+ , template-haskell >=2.8 && <2.18+ , text >=1.2.3.0 && <1.3+ if flag(bytestring-builder)- build-depends: bytestring >= 0.9 && < 0.10.4- , bytestring-builder >= 0.10.4 && < 0.11+ build-depends:+ bytestring >=0.9 && <0.10.4+ , bytestring-builder >=0.10.4 && <0.11+ else- build-depends: bytestring >= 0.10.4+ build-depends: bytestring >=0.10.4 && <0.12 - if flag(integer-simple)- build-depends: integer-simple+ if impl(ghc >=9.0)+ build-depends: base >=4.15++ if flag(integer-simple)+ build-depends: invalid-cabal-flag-settings <0+ else+ if flag(integer-simple)+ build-depends: integer-simple++ else build-depends: integer-gmp - hs-source-dirs: src- default-language: Haskell2010+ if impl(ghc <8)+ other-extensions: TemplateHaskell + if impl(ghc >= 9.0)+ -- these flags may abort compilation with GHC-8.10+ -- https://gitlab.haskell.org/ghc/ghc/-/merge_requests/3295+ ghc-options: -Winferred-safe-imports -Wmissing-safe-haskell-mode++ hs-source-dirs: src+ default-language: Haskell2010+ test-suite test-scientific type: exitcode-stdio-1.0 hs-source-dirs: test main-is: test.hs default-language: Haskell2010 ghc-options: -Wall-- build-depends: scientific- , base >= 4.3 && < 5- , binary >= 0.4.1- , tasty >= 0.5- , tasty-ant-xml >= 1.0- , tasty-hunit >= 0.8- , tasty-smallcheck >= 0.2- , tasty-quickcheck >= 0.8- , smallcheck >= 1.0- , QuickCheck >= 2.5- , text >= 0.8+ build-depends:+ base+ , binary+ , QuickCheck >=2.14.2+ , scientific+ , smallcheck >=1.0+ , tasty >=1.4.0.1+ , tasty-hunit >=0.8+ , tasty-quickcheck >=0.8+ , tasty-smallcheck >=0.2+ , text if flag(bytestring-builder)- build-depends: bytestring >= 0.9 && < 0.10.4- , bytestring-builder >= 0.10.4 && < 0.11+ build-depends:+ bytestring+ , bytestring-builder+ else- build-depends: bytestring >= 0.10.4+ build-depends: bytestring benchmark bench-scientific type: exitcode-stdio-1.0@@ -127,6 +160,7 @@ main-is: bench.hs default-language: Haskell2010 ghc-options: -O2- build-depends: scientific- , base >= 4.3 && < 5- , criterion >= 0.5+ build-depends:+ base+ , criterion >=0.5+ , scientific
src/Data/ByteString/Builder/Scientific.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, OverloadedStrings #-}+{-# LANGUAGE CPP, OverloadedStrings, Safe #-} module Data.ByteString.Builder.Scientific ( scientificBuilder
src/Data/Scientific.hs view
@@ -4,7 +4,15 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE PatternGuards #-}+{-# LANGUAGE Trustworthy #-} +#if __GLASGOW_HASKELL__ >= 800+{-# LANGUAGE DeriveLift #-}+{-# LANGUAGE StandaloneDeriving #-}+#else+{-# LANGUAGE TemplateHaskell #-}+#endif+ -- | -- Module : Data.Scientific -- Copyright : Bas van Dijk 2013@@ -48,7 +56,7 @@ -- -- This module is designed to be imported qualified: ----- @import Data.Scientific as Scientific@+-- @import qualified Data.Scientific as Scientific@ module Data.Scientific ( Scientific @@ -98,7 +106,6 @@ import Control.Exception (throw, ArithException(DivideByZero)) import Control.Monad (mplus)-import Control.Monad.ST (runST) import Control.DeepSeq (NFData, rnf) import Data.Binary (Binary, get, put) import Data.Char (intToDigit, ord)@@ -108,7 +115,6 @@ import qualified Data.Map as M (Map, empty, insert, lookup) import Data.Ratio ((%), numerator, denominator) import Data.Typeable (Typeable)-import qualified Data.Primitive.Array as Primitive import Data.Word (Word8, Word16, Word32, Word64) import Math.NumberTheory.Logarithms (integerLog10') import qualified Numeric (floatToDigits)@@ -129,16 +135,10 @@ import Control.Applicative ((<*>)) #endif -#if MIN_VERSION_base(4,5,0)-import Data.Bits (unsafeShiftR)-#else-import Data.Bits (shiftR)-#endif--import GHC.Integer (quotRemInteger, quotInteger)-import GHC.Integer.Compat (divInteger)-import Utils (roundTo)+import GHC.Integer.Compat (quotRemInteger, quotInteger, divInteger)+import Utils (maxExpt, roundTo, magnitude) +import Language.Haskell.TH.Syntax (Lift (..)) ---------------------------------------------------------------------- -- Type@@ -163,6 +163,20 @@ -- in 'toDecimalDigits'. -- -- Use 'normalize' to do manual normalization.+ --+ -- /WARNING:/ 'coefficient' and 'base10exponent' violate+ -- substantivity of 'Eq'.+ --+ -- >>> let x = scientific 1 2+ -- >>> let y = scientific 100 0+ -- >>> x == y+ -- True+ --+ -- but+ --+ -- >>> (coefficient x == coefficient y, base10Exponent x == base10Exponent y)+ -- (False,False)+ -- , base10Exponent :: {-# UNPACK #-} !Int -- ^ The base-10 exponent of a scientific number.@@ -173,17 +187,32 @@ scientific :: Integer -> Int -> Scientific scientific = Scientific - ---------------------------------------------------------------------- -- Instances ---------------------------------------------------------------------- +#if __GLASGOW_HASKELL__ >= 800+-- | @since 0.3.7.0+deriving instance Lift Scientific+#else+instance Lift Scientific where+ lift (Scientific c e) = [| Scientific c e |]+#endif++ instance NFData Scientific where rnf (Scientific _ _) = () -- | A hash can be safely calculated from a @Scientific@. No magnitude @10^e@ is -- calculated so there's no risk of a blowup in space or time when hashing -- scientific numbers coming from untrusted sources.+--+-- >>> import Data.Hashable (hash)+-- >>> let x = scientific 1 2+-- >>> let y = scientific 100 0+-- >>> (x == y, hash x == hash y)+-- (True,True)+-- instance Hashable Scientific where hashWithSalt salt s = salt `hashWithSalt` c `hashWithSalt` e where@@ -295,15 +324,23 @@ -- | /WARNING:/ 'recip' and '/' will throw an error when their outputs are -- <https://en.wikipedia.org/wiki/Repeating_decimal repeating decimals>. --+-- These methods also compute 'Integer' magnitudes (@10^e@). If these methods+-- are applied to arguments which have huge exponents this could fill up all+-- space and crash your program! So don't apply these methods to scientific+-- numbers coming from untrusted sources.+-- -- 'fromRational' will throw an error when the input 'Rational' is a repeating -- decimal. Consider using 'fromRationalRepetend' for these rationals which -- will detect the repetition and indicate where it starts. instance Fractional Scientific where recip = fromRational . recip . toRational- {-# INLINABLE recip #-} - x / y = fromRational $ toRational x / toRational y- {-# INLINABLE (/) #-}+ Scientific c1 e1 / Scientific c2 e2+ | d < 0 = fromRational (x / (fromInteger (magnitude (-d))))+ | otherwise = fromRational (x * fromInteger (magnitude d))+ where+ d = e1 - e2+ x = c1 % c2 fromRational rational = case mbRepetendIx of@@ -651,48 +688,10 @@ {-# INLINE toIntegral #-} -------------------------------------------------------------------------- Exponentiation with a cache for the most common numbers.----------------------------------------------------------------------- --- | The same limit as in GHC.Float.-maxExpt :: Int-maxExpt = 324 -expts10 :: Primitive.Array Integer-expts10 = runST $ do- ma <- Primitive.newArray maxExpt uninitialised- Primitive.writeArray ma 0 1- Primitive.writeArray ma 1 10- let go !ix- | ix == maxExpt = Primitive.unsafeFreezeArray ma- | otherwise = do- Primitive.writeArray ma ix xx- Primitive.writeArray ma (ix+1) (10*xx)- go (ix+2)- where- xx = x * x- x = Primitive.indexArray expts10 half-#if MIN_VERSION_base(4,5,0)- !half = ix `unsafeShiftR` 1-#else- !half = ix `shiftR` 1-#endif- go 2 -uninitialised :: error-uninitialised = error "Data.Scientific: uninitialised element" --- | @magnitude e == 10 ^ e@-magnitude :: Num a => Int -> a-magnitude e | e < maxExpt = cachedPow10 e- | otherwise = cachedPow10 hi * 10 ^ (e - hi)- where- cachedPow10 = fromInteger . Primitive.indexArray expts10-- hi = maxExpt - 1-- ---------------------------------------------------------------------- -- Conversions ----------------------------------------------------------------------@@ -979,11 +978,16 @@ -- | See 'formatScientific' if you need more control over the rendering. instance Show Scientific where- show s | coefficient s < 0 = '-':showPositive (-s)- | otherwise = showPositive s+ showsPrec d s+ | coefficient s < 0 = showParen (d > prefixMinusPrec) $+ showChar '-' . showPositive (-s)+ | otherwise = showPositive s where- showPositive :: Scientific -> String- showPositive = fmtAsGeneric . toDecimalDigits+ prefixMinusPrec :: Int+ prefixMinusPrec = 6++ showPositive :: Scientific -> ShowS+ showPositive = showString . fmtAsGeneric . toDecimalDigits fmtAsGeneric :: ([Int], Int) -> String fmtAsGeneric x@(_is, e)
src/Data/Text/Lazy/Builder/Scientific.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, OverloadedStrings #-}+{-# LANGUAGE CPP, OverloadedStrings, Safe #-} module Data.Text.Lazy.Builder.Scientific ( scientificBuilder
src/GHC/Integer/Compat.hs view
@@ -1,7 +1,14 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE Trustworthy #-} -module GHC.Integer.Compat (divInteger) where+module GHC.Integer.Compat (divInteger, quotRemInteger, quotInteger) where +import GHC.Integer (quotRemInteger, quotInteger)++#if MIN_VERSION_base(4,15,0)+import GHC.Integer (divInteger)+#else+ #ifdef MIN_VERSION_integer_simple #if MIN_VERSION_integer_simple(0,1,1)@@ -20,4 +27,5 @@ divInteger = div #endif +#endif #endif
src/Utils.hs view
@@ -1,14 +1,28 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE Trustworthy #-}+{-# LANGUAGE ScopedTypeVariables #-} module Utils ( roundTo , i2d+ , maxExpt+ , magnitude ) where import GHC.Base (Int(I#), Char(C#), chr#, ord#, (+#)) +import qualified Data.Primitive.Array as Primitive+import Control.Monad.ST (runST)++#if MIN_VERSION_base(4,5,0)+import Data.Bits (unsafeShiftR)+#else+import Data.Bits (shiftR)+#endif+ roundTo :: Int -> [Int] -> (Int, [Int]) roundTo d is = case f d True is of@@ -34,3 +48,44 @@ {-# INLINE i2d #-} i2d :: Int -> Char i2d (I# i#) = C# (chr# (ord# '0'# +# i# ))++----------------------------------------------------------------------+-- Exponentiation with a cache for the most common numbers.+----------------------------------------------------------------------++-- | The same limit as in GHC.Float.+maxExpt :: Int+maxExpt = 324++expts10 :: Primitive.Array Integer+expts10 = runST $ do+ ma <- Primitive.newArray maxExpt uninitialised+ Primitive.writeArray ma 0 1+ Primitive.writeArray ma 1 10+ let go !ix+ | ix == maxExpt = Primitive.unsafeFreezeArray ma+ | otherwise = do+ Primitive.writeArray ma ix xx+ Primitive.writeArray ma (ix+1) (10*xx)+ go (ix+2)+ where+ xx = x * x+ x = Primitive.indexArray expts10 half+#if MIN_VERSION_base(4,5,0)+ !half = ix `unsafeShiftR` 1+#else+ !half = ix `shiftR` 1+#endif+ go 2++uninitialised :: error+uninitialised = error "Data.Scientific: uninitialised element"++-- | @magnitude e == 10 ^ e@+magnitude :: Num a => Int -> a+magnitude e | e < maxExpt = cachedPow10 e+ | otherwise = cachedPow10 hi * 10 ^ (e - hi)+ where+ cachedPow10 = fromInteger . Primitive.indexArray expts10++ hi = maxExpt - 1
test/test.hs view
@@ -18,7 +18,6 @@ import Data.Word import Data.Scientific as Scientific import Test.Tasty-import Test.Tasty.Runners.AntXML import Test.Tasty.HUnit (testCase, (@?=), Assertion, assertBool) import qualified Test.SmallCheck as SC import qualified Test.SmallCheck.Series as SC@@ -114,6 +113,9 @@ , testGroup "Formatting" [ testProperty "read . show == id" $ \s -> read (show s) === s+ , testCase "show (Just 1)" $ testShow (Just 1) "Just 1.0"+ , testCase "show (Just 0)" $ testShow (Just 0) "Just 0.0"+ , testCase "show (Just (-1))" $ testShow (Just (-1)) "Just (-1.0)" , testGroup "toDecimalDigits" [ smallQuick "laws"@@ -279,13 +281,16 @@ ] testMain :: TestTree -> IO ()-testMain = defaultMainWithIngredients (antXMLRunner:defaultIngredients)+testMain = defaultMainWithIngredients defaultIngredients testReads :: String -> [(Scientific, String)] -> Assertion testReads inp out = reads inp @?= out testRead :: String -> Scientific -> Assertion testRead inp out = read inp @?= out++testShow :: Maybe Scientific -> String -> Assertion+testShow inp out = show inp @?= out testScientificP :: String -> [(Scientific, String)] -> Assertion testScientificP inp out = readP_to_S Scientific.scientificP inp @?= out