packages feed

scientific-notation 0.1.6.1 → 0.1.7.0

raw patch · 4 files changed

+68/−5 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.Number.Scientific: toInteger :: Scientific -> Maybe Integer

Files

CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for scientific-notation +## 0.1.7.0 -- 2024-02-12++* Add `toInteger`.+ ## 0.1.6.1 -- 2024-02-06  * Update package metadata.
scientific-notation.cabal view
@@ -1,6 +1,6 @@-cabal-version:   2.2+cabal-version:   2.4 name:            scientific-notation-version:         0.1.6.1+version:         0.1.7.0 synopsis:        Scientific notation intended for tokenization description:   This library provides a type used to represent a number in@@ -45,6 +45,7 @@ copyright:       2019 Andrew Martin category:        Data extra-doc-files: CHANGELOG.md+tested-with:     GHC ==9.4.8 || ==9.6.3 || ==9.8.1  common build-settings   default-language: Haskell2010
src/Data/Number/Scientific.hs view
@@ -34,6 +34,7 @@   , toInt   , toInt32   , toInt64+  , toInteger   , withExposed      -- * Scale and Consume@@ -59,7 +60,7 @@   , builderUtf8   ) where -import Prelude hiding (negate)+import Prelude hiding (negate,toInteger)  import Control.Monad.ST (runST) import Data.ByteString.Short.Internal (ShortByteString (SBS))@@ -237,6 +238,53 @@   (# (# #) | #) -> Nothing   (# | i #) -> Just (I64# (intToInt64# i)) +-- | This can exhaust memory. Do not use on untrusted input.+toInteger :: Scientific -> Maybe Integer+toInteger (Scientific coeff e largeNum)+  | e == minBound = case largeNum of+      LargeScientific bigCoeff bigExp -> case compare bigExp 0 of+        GT -> Just (bigCoeff * ((10 :: Integer) ^ bigExp))+        EQ -> Just bigCoeff+        LT -> case attemptLargeNegativeExponentiate bigCoeff (Prelude.negate bigExp) of+          Nothing -> Nothing+          Just i -> Just i+  | otherwise = case compare e 0 of+      GT -> Just (Prelude.toInteger coeff * ((10 :: Integer) ^ e))+      EQ -> Just (Prelude.toInteger coeff)+      LT -> case attemptNegativeExponentiate coeff (Prelude.negate e) of+        Nothing -> Nothing+        Just i -> Just (Prelude.toInteger i)++-- The exponent argument must be non-negative, but we interpret it as+-- a negative number.+attemptNegativeExponentiate :: Int -> Int -> Maybe Int+attemptNegativeExponentiate c0 e0 = go c0 e0 where+  -- Note: This is unoptimized and has poor performance.+  go :: Int -> Int -> Maybe Int+  go !c !e = case compare e 0 of+    EQ -> Just c+    GT ->+      let c' = div c 10+       in if c' * 10 == c+            then go c' (e - 1)+            else Nothing+    LT -> errorWithoutStackTrace "attemptNegativeExponentiate: invariant violated"++-- The exponent argument must be non-negative, but we interpret it as+-- a negative number.+attemptLargeNegativeExponentiate :: Integer -> Integer -> Maybe Integer+attemptLargeNegativeExponentiate c0 e0 = go c0 e0 where+  -- Note: This is unoptimized and has poor performance.+  go :: Integer -> Integer -> Maybe Integer+  go !c !e = case compare e 0 of+    EQ -> Just c+    GT ->+      let c' = div c 10+       in if c' * 10 == c+            then go c' (e - 1)+            else Nothing+    LT -> errorWithoutStackTrace "attemptLargeNegativeExponentiate: invariant violated"+ {- | This works even if the number has a fractional component. For example:  >>> roundShiftedToInt64 2 (fromFixed @E3 1.037)@@ -699,7 +747,7 @@                 else roundNegIntegerNegExp10 coefficient (fromInteger expon)   | otherwise = (# (# #) | #)  where-  exponent1 = exponent0 + toInteger adj+  exponent1 = exponent0 + Prelude.toInteger adj  -- Precondition: the exponent is non-negative. This returns -- an unboxed Nothing on overflow. This implementation should
test/Main.hs view
@@ -4,13 +4,15 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} +import Prelude hiding (toInteger)+ import Control.Monad (replicateM, when) import Data.Bool (bool) import Data.Bytes.Types (Bytes (Bytes)) import Data.Char (ord) import Data.Fixed (E12, Fixed) import Data.Int (Int64)-import Data.Number.Scientific (large, roundShiftedToInt64, small, toInt32, toInt64, toWord16, toWord32, toWord64, toWord8)+import Data.Number.Scientific (large, roundShiftedToInt64, small, toInt32, toInt64, toWord16, toWord32, toWord64, toWord8, toInteger) import Data.Primitive (ByteArray) import Data.Word (Word8) import Test.Tasty (TestTree, defaultMain, testGroup)@@ -146,6 +148,14 @@         , THU.testCase "AD" $ Just 0 @=? roundShiftedToInt64 0 (large (50000000000000000000000000000) (-1_000_000_000))         , THU.testCase "AE" $ Just 2 @=? toInt64 (small 2 0)         , THU.testCase "AF" $ Just 2 @=? toInt64 (large 2 0)+        ]+    , testGroup+        "Integer"+        [ THU.testCase "A" $ Just 30 @=? toInteger (small 300 (-1))+        , THU.testCase "B" $ Just 300 @=? toInteger (small 300 0)+        , THU.testCase "C" $ Just 65535 @=? toInteger (large 65535e40 (-40))+        , THU.testCase "D" $ Just 999_999_999_999_999_999_999_999_999_999_999_000 @=? toInteger (large 999_999_999_999_999_999_999_999_999_999_999 3)+        , THU.testCase "E" $ Just 999_999_999_999_999_999_999_999_999_999_999 @=? toInteger (large 999_999_999_999_999_999_999_999_999_999_999_000 (-3))         ]     , testGroup         "Compare"