double-conversion 0.1.0.0 → 0.2.0.0
raw patch · 9 files changed
+318/−147 lines, 9 filesdep +bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: bytestring
API changes (from Hackage documentation)
- Data.Double.Conversion: toExponential :: Int -> Double -> Text
- Data.Double.Conversion: toFixed :: Int -> Double -> Text
- Data.Double.Conversion: toPrecision :: Int -> Double -> Text
- Data.Double.Conversion: toShortest :: Double -> Text
+ Data.Double.Conversion.ByteString: toExponential :: Int -> Double -> ByteString
+ Data.Double.Conversion.ByteString: toFixed :: Int -> Double -> ByteString
+ Data.Double.Conversion.ByteString: toPrecision :: Int -> Double -> ByteString
+ Data.Double.Conversion.ByteString: toShortest :: Double -> ByteString
+ Data.Double.Conversion.Text: toExponential :: Int -> Double -> Text
+ Data.Double.Conversion.Text: toFixed :: Int -> Double -> Text
+ Data.Double.Conversion.Text: toPrecision :: Int -> Double -> Text
+ Data.Double.Conversion.Text: toShortest :: Double -> Text
Files
- Data/Double/Conversion.hs +0/−100
- Data/Double/Conversion/ByteString.hs +76/−0
- Data/Double/Conversion/FFI.hs +70/−0
- Data/Double/Conversion/Text.hs +79/−0
- benchmarks/Benchmarks.hs +17/−9
- benchmarks/double-conversion-benchmarks.cabal +1/−0
- cbits/hs-double-conversion.cc +44/−29
- double-conversion.cabal +23/−5
- include/hs-double-conversion.h +8/−4
− Data/Double/Conversion.hs
@@ -1,100 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface, MagicHash, Rank2Types,- UnliftedFFITypes #-}---- |--- Module : Data.Double.Conversion--- Copyright : (c) 2011 MailRank, Inc.------ License : BSD-style--- Maintainer : bos@mailrank.com--- Stability : experimental--- Portability : GHC------ Fast, efficient support for converting between double precision--- floating point values and text.--module Data.Double.Conversion- (- toExponential- , toFixed- , toPrecision- , toShortest- ) where--import Control.Monad (when)-import Control.Monad.ST (unsafeIOToST, runST)-import Data.Text.Internal (Text(Text))-import Foreign.C.Types (CDouble, CInt)-import GHC.Prim (MutableByteArray#)-import qualified Data.Text.Array as A---- | Compute a representation in exponential format with the requested--- number of digits after the decimal point. The last emitted digit is--- rounded. If -1 digits are requested, then the shortest exponential--- representation is computed.-toExponential :: Int -> Double -> Text-toExponential ndigits = convert "toExponential" len $ \val mba ->- c_ToExponential val mba (fromIntegral ndigits)- where len = c_ToExponentialLength- {-# NOINLINE len #-}---- | Compute a decimal representation with a fixed number of digits--- after the decimal point. The last emitted digit is rounded.-toFixed :: Int -> Double -> Text-toFixed ndigits = convert "toFixed" len $ \val mba ->- c_ToFixed val mba (fromIntegral ndigits)- where len = c_ToFixedLength- {-# NOINLINE len #-}---- | Compute the shortest string of digits that correctly represent--- the input number.-toShortest :: Double -> Text-toShortest = convert "toShortest" len c_ToShortest- where len = c_ToShortestLength- {-# NOINLINE len #-}---- | Compute @precision@ leading digits of the given value either in--- exponential or decimal format. The last computed digit is rounded.-toPrecision :: Int -> Double -> Text-toPrecision ndigits = convert "toPrecision" len $ \val mba ->- c_ToPrecision val mba (fromIntegral ndigits)- where len = c_ToPrecisionLength- {-# NOINLINE len #-}--convert :: String -> CInt- -> (forall s. CDouble -> MutableByteArray# s -> IO CInt)- -> Double -> Text-convert func len act val = runST go- where- go = do- buf <- A.new (fromIntegral len)- size <- unsafeIOToST $ act (realToFrac val) (A.maBA buf)- when (size == -1) .- fail $ "Data.Double.Conversion." ++ func ++- ": conversion failed (invalid precision requested)"- frozen <- A.unsafeFreeze buf- return $ Text frozen 0 (fromIntegral size)--foreign import ccall unsafe "hs-double-conversion.h _hs_ToShortestLength"- c_ToShortestLength :: CInt--foreign import ccall unsafe "hs-double-conversion.h _hs_ToShortest"- c_ToShortest :: CDouble -> MutableByteArray# s -> IO CInt--foreign import ccall unsafe "hs-double-conversion.h _hs_ToFixedLength"- c_ToFixedLength :: CInt--foreign import ccall unsafe "hs-double-conversion.h _hs_ToFixed"- c_ToFixed :: CDouble -> MutableByteArray# s -> CInt -> IO CInt--foreign import ccall unsafe "hs-double-conversion.h _hs_ToExponentialLength"- c_ToExponentialLength :: CInt--foreign import ccall unsafe "hs-double-conversion.h _hs_ToExponential"- c_ToExponential :: CDouble -> MutableByteArray# s -> CInt -> IO CInt--foreign import ccall unsafe "hs-double-conversion.h _hs_ToPrecisionLength"- c_ToPrecisionLength :: CInt--foreign import ccall unsafe "hs-double-conversion.h _hs_ToPrecision"- c_ToPrecision :: CDouble -> MutableByteArray# s -> CInt -> IO CInt
+ Data/Double/Conversion/ByteString.hs view
@@ -0,0 +1,76 @@+-- |+-- Module : Data.Double.Conversion.ByteString+-- Copyright : (c) 2011 MailRank, Inc.+--+-- License : BSD-style+-- Maintainer : bos@mailrank.com+-- Stability : experimental+-- Portability : GHC+--+-- Fast, efficient support for converting between double precision+-- floating point values and text.+--+-- Although about 15 times faster than plain 'show', these functions+-- are /slower/ than their 'Text' counterparts, at roughly half the+-- speed. (This seems to be due to the cost of allocating+-- 'ByteString' values via @malloc@.)++module Data.Double.Conversion.ByteString+ (+ toExponential+ , toFixed+ , toPrecision+ , toShortest+ ) where++import Control.Monad (when)+import Foreign.ForeignPtr (withForeignPtr)+import Data.Double.Conversion.FFI+import Data.Word (Word8)+import Data.ByteString.Internal (ByteString(..), mallocByteString)+import Foreign.C.Types (CDouble, CInt)+import Foreign.Ptr (Ptr)+import System.IO.Unsafe (unsafePerformIO)++-- | Compute a representation in exponential format with the requested+-- number of digits after the decimal point. The last emitted digit is+-- rounded. If -1 digits are requested, then the shortest exponential+-- representation is computed.+toExponential :: Int -> Double -> ByteString+toExponential ndigits = convert "toExponential" len $ \val mba ->+ c_ToExponential val mba (fromIntegral ndigits)+ where len = c_ToExponentialLength+ {-# NOINLINE len #-}++-- | Compute a decimal representation with a fixed number of digits+-- after the decimal point. The last emitted digit is rounded.+toFixed :: Int -> Double -> ByteString+toFixed ndigits = convert "toFixed" len $ \val mba ->+ c_ToFixed val mba (fromIntegral ndigits)+ where len = c_ToFixedLength+ {-# NOINLINE len #-}++-- | Compute the shortest string of digits that correctly represent+-- the input number.+toShortest :: Double -> ByteString+toShortest = convert "toShortest" len c_ToShortest+ where len = c_ToShortestLength+ {-# NOINLINE len #-}++-- | Compute @precision@ leading digits of the given value either in+-- exponential or decimal format. The last computed digit is rounded.+toPrecision :: Int -> Double -> ByteString+toPrecision ndigits = convert "toPrecision" len $ \val mba ->+ c_ToPrecision val mba (fromIntegral ndigits)+ where len = c_ToPrecisionLength+ {-# NOINLINE len #-}++convert :: String -> CInt -> (CDouble -> Ptr Word8 -> IO CInt)+ -> Double -> ByteString+convert func len act val = unsafePerformIO $ do+ fp <- mallocByteString (fromIntegral len)+ size <- withForeignPtr fp $ act (realToFrac val)+ when (size == -1) .+ fail $ "Data.Double.Conversion.ByteString." ++ func +++ ": conversion failed (invalid precision requested)"+ return $ PS fp 0 (fromIntegral size)
+ Data/Double/Conversion/FFI.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE ForeignFunctionInterface, MagicHash, UnliftedFFITypes #-}++-- |+-- Module : Data.Double.Conversion.FFI+-- Copyright : (c) 2011 MailRank, Inc.+--+-- License : BSD-style+-- Maintainer : bos@mailrank.com+-- Stability : experimental+-- Portability : GHC+--+-- FFI interface support for converting between double precision+-- floating point values and text.++module Data.Double.Conversion.FFI+ (+ c_Text_ToExponential+ , c_Text_ToFixed+ , c_Text_ToPrecision+ , c_Text_ToShortest+ , c_ToExponentialLength+ , c_ToFixedLength+ , c_ToPrecisionLength+ , c_ToShortestLength+ , c_ToExponential+ , c_ToFixed+ , c_ToPrecision+ , c_ToShortest+ ) where++import Data.Word (Word8)+import Foreign.C.Types (CDouble, CInt)+import Foreign.Ptr (Ptr)+import GHC.Prim (MutableByteArray#)++foreign import ccall unsafe "hs-double-conversion.h _hs_ToShortestLength"+ c_ToShortestLength :: CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_Text_ToShortest"+ c_Text_ToShortest :: CDouble -> MutableByteArray# s -> IO CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_ToShortest"+ c_ToShortest :: CDouble -> Ptr Word8 -> IO CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_ToFixedLength"+ c_ToFixedLength :: CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_Text_ToFixed"+ c_Text_ToFixed :: CDouble -> MutableByteArray# s -> CInt -> IO CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_ToFixed"+ c_ToFixed :: CDouble -> Ptr Word8 -> CInt -> IO CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_ToExponentialLength"+ c_ToExponentialLength :: CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_Text_ToExponential"+ c_Text_ToExponential :: CDouble -> MutableByteArray# s -> CInt -> IO CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_ToExponential"+ c_ToExponential :: CDouble -> Ptr Word8 -> CInt -> IO CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_ToPrecisionLength"+ c_ToPrecisionLength :: CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_Text_ToPrecision"+ c_Text_ToPrecision :: CDouble -> MutableByteArray# s -> CInt -> IO CInt++foreign import ccall unsafe "hs-double-conversion.h _hs_ToPrecision"+ c_ToPrecision :: CDouble -> Ptr Word8 -> CInt -> IO CInt
+ Data/Double/Conversion/Text.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE MagicHash, Rank2Types #-}++-- |+-- Module : Data.Double.Conversion.Text+-- Copyright : (c) 2011 MailRank, Inc.+--+-- License : BSD-style+-- Maintainer : bos@mailrank.com+-- Stability : experimental+-- Portability : GHC+--+-- Fast, efficient support for converting between double precision+-- floating point values and text.+--+-- These functions are about 30 times faster than the default 'show'+-- implementation for the 'Double' type.++module Data.Double.Conversion.Text+ (+ toExponential+ , toFixed+ , toPrecision+ , toShortest+ ) where++import Control.Monad (when)+import Control.Monad.ST (unsafeIOToST, runST)+import Data.Double.Conversion.FFI+import Data.Text.Internal (Text(Text))+import Foreign.C.Types (CDouble, CInt)+import GHC.Prim (MutableByteArray#)+import qualified Data.Text.Array as A++-- | Compute a representation in exponential format with the requested+-- number of digits after the decimal point. The last emitted digit is+-- rounded. If -1 digits are requested, then the shortest exponential+-- representation is computed.+toExponential :: Int -> Double -> Text+toExponential ndigits = convert "toExponential" len $ \val mba ->+ c_Text_ToExponential val mba (fromIntegral ndigits)+ where len = c_ToExponentialLength+ {-# NOINLINE len #-}++-- | Compute a decimal representation with a fixed number of digits+-- after the decimal point. The last emitted digit is rounded.+toFixed :: Int -> Double -> Text+toFixed ndigits = convert "toFixed" len $ \val mba ->+ c_Text_ToFixed val mba (fromIntegral ndigits)+ where len = c_ToFixedLength+ {-# NOINLINE len #-}++-- | Compute the shortest string of digits that correctly represent+-- the input number.+toShortest :: Double -> Text+toShortest = convert "toShortest" len c_Text_ToShortest+ where len = c_ToShortestLength+ {-# NOINLINE len #-}++-- | Compute @precision@ leading digits of the given value either in+-- exponential or decimal format. The last computed digit is rounded.+toPrecision :: Int -> Double -> Text+toPrecision ndigits = convert "toPrecision" len $ \val mba ->+ c_Text_ToPrecision val mba (fromIntegral ndigits)+ where len = c_ToPrecisionLength+ {-# NOINLINE len #-}++convert :: String -> CInt+ -> (forall s. CDouble -> MutableByteArray# s -> IO CInt)+ -> Double -> Text+convert func len act val = runST go+ where+ go = do+ buf <- A.new (fromIntegral len)+ size <- unsafeIOToST $ act (realToFrac val) (A.maBA buf)+ when (size == -1) .+ fail $ "Data.Double.Conversion.Text." ++ func +++ ": conversion failed (invalid precision requested)"+ frozen <- A.unsafeFreeze buf+ return $ Text frozen 0 (fromIntegral size)
benchmarks/Benchmarks.hs view
@@ -1,20 +1,28 @@ {-# LANGUAGE ForeignFunctionInterface, OverloadedStrings #-} import Criterion.Main-import Data.Double.Conversion+import qualified Data.Double.Conversion.ByteString as B+import qualified Data.Double.Conversion.Text as T import Foreign.C.Types (CInt, CDouble) import qualified Data.Text as T--showText :: Double -> T.Text-showText d = T.pack (show d)+import qualified Text.Show.ByteString as BS main = defaultMain [ bgroup "haskell" [- bench "show" $ whnf showText pi- , bench "toShortest" $ whnf toShortest pi- , bench "toExponential" $ whnf (toExponential 3) pi- , bench "toPrecision" $ whnf (toExponential 8) pi- , bench "toFixed" $ whnf (toFixed 8) pi+ bench "show" $ nf show (pi::Double)+ , bench "bytestring-show" $ whnf BS.show (pi::Double)+ , bgroup "text" [+ bench "toShortest" $ whnf T.toShortest pi+ , bench "toExponential" $ whnf (T.toExponential 3) pi+ , bench "toPrecision" $ whnf (T.toExponential 8) pi+ , bench "toFixed" $ whnf (T.toFixed 8) pi+ ]+ , bgroup "bytestring" [+ bench "toShortest" $ whnf B.toShortest pi+ , bench "toExponential" $ whnf (B.toExponential 3) pi+ , bench "toPrecision" $ whnf (B.toExponential 8) pi+ , bench "toFixed" $ whnf (B.toFixed 8) pi+ ] ] , bgroup "sprintf" [ bench "exact" $ whnf sprintf_exact pi
benchmarks/double-conversion-benchmarks.cabal view
@@ -10,6 +10,7 @@ build-depends: base,+ bytestring-show, criterion >= 0.5.0.10, double-conversion, text >= 0.11.0.8
cbits/hs-double-conversion.cc view
@@ -48,6 +48,13 @@ return pos; } +static int copy(uint16_t *buf, const char *cbuf, const int len)+{+ for (int i = 0; i < len; i++)+ buf[i] = cbuf[i];+ return len;+}+ static inline const DoubleToStringConverter& defaultConverter(void) { const int flags = DoubleToStringConverter::UNIQUE_ZERO;@@ -61,53 +68,61 @@ } extern "C"-int _hs_ToShortest(double value, uint16_t *buf)+int _hs_ToShortest(double value, char *buf) {- char cbuf[kToShortestLength];- StringBuilder builder(cbuf, kToShortestLength);- bool ok = defaultConverter().ToShortest(value, &builder);+ StringBuilder builder(buf, kToShortestLength);+ return defaultConverter().ToShortest(value, &builder)+ ? builder.position() : -1;+} - if (!ok)- return -1;+extern "C"+int _hs_Text_ToShortest(double value, uint16_t *buf)+{+ char cbuf[kToShortestLength];+ return copy(buf, cbuf, _hs_ToShortest(value, cbuf));+} - return copy(buf, builder, cbuf);+extern "C"+int _hs_ToFixed(double value, char *buf, const int ndigits)+{+ StringBuilder builder(buf, kToFixedLength);+ return defaultConverter().ToFixed(value, ndigits, &builder)+ ? builder.position() : -1; } extern "C"-int _hs_ToFixed(double value, uint16_t *buf, const int ndigits)+int _hs_Text_ToFixed(double value, uint16_t *buf, const int ndigits) { char cbuf[kToFixedLength];- StringBuilder builder(cbuf, kToFixedLength);- bool ok = defaultConverter().ToFixed(value, ndigits, &builder);+ return copy(buf, cbuf, _hs_ToFixed(value, cbuf, ndigits));+} - if (!ok)- return -1;- - return copy(buf, builder, cbuf);+extern "C"+int _hs_ToExponential(double value, char *buf, const int ndigits)+{+ StringBuilder builder(buf, kToExponentialLength);+ return defaultConverter().ToExponential(value, ndigits, &builder)+ ? builder.position() : -1; } extern "C"-int _hs_ToExponential(double value, uint16_t *buf, const int ndigits)+int _hs_Text_ToExponential(double value, uint16_t *buf, const int ndigits) { char cbuf[kToExponentialLength];- StringBuilder builder(cbuf, kToExponentialLength);- bool ok = defaultConverter().ToExponential(value, ndigits, &builder);+ return copy(buf, cbuf, _hs_ToExponential(value, cbuf, ndigits));+} - if (!ok)- return -1;- - return copy(buf, builder, cbuf);+extern "C"+int _hs_ToPrecision(double value, char *buf, const int precision)+{+ StringBuilder builder(buf, kToPrecisionLength);+ return defaultConverter().ToPrecision(value, precision, &builder)+ ? builder.position() : -1; } extern "C"-int _hs_ToPrecision(double value, uint16_t *buf, const int precision)+int _hs_Text_ToPrecision(double value, uint16_t *buf, const int precision) { char cbuf[kToPrecisionLength];- StringBuilder builder(cbuf, kToPrecisionLength);- bool ok = defaultConverter().ToPrecision(value, precision, &builder);-- if (!ok)- return -1;- - return copy(buf, builder, cbuf);+ return copy(buf, cbuf, _hs_ToPrecision(value, cbuf, precision)); }
double-conversion.cabal view
@@ -1,5 +1,5 @@ name: double-conversion-version: 0.1.0.0+version: 0.2.0.0 license: BSD3 license-file: LICENSE homepage: https://github.com/mailrank/double-conversion@@ -13,9 +13,22 @@ cabal-version: >= 1.8 build-type: Simple description:- A library that performs fast conversion between double precision- floating point and text, implemented as bindings to the C++- double-conversion library written by Florian Loitsch at Google.+ A library that performs fast, accurate conversion between double+ precision floating point and text.+ .+ This library is implemented as bindings to the C+++ @double-conversion@ library written by Florian Loitsch at Google:+ <http://code.google.com/p/double-conversion/>.+ .+ The 'Text' versions of these functions are about 30 times faster+ than the default 'show' implementation for the 'Double' type.+ .+ The 'ByteString' versions are /slower/ than the 'Text' versions;+ roughly half the speed. (This seems to be due to the cost of+ allocating 'ByteString' values via @malloc@.)+ .+ As a final note, be aware that the @bytestring-show@ package is+ about 50% slower than simply using 'show'. extra-source-files: README.markdown@@ -59,10 +72,15 @@ include exposed-modules:- Data.Double.Conversion+ Data.Double.Conversion.ByteString+ Data.Double.Conversion.Text + other-modules:+ Data.Double.Conversion.FFI+ build-depends: base == 4.*,+ bytestring, ghc-prim, text >= 0.11.0.8
include/hs-double-conversion.h view
@@ -9,13 +9,17 @@ #include <stddef.h> int _hs_ToShortestLength(void);-int _hs_ToShortest(double value, uint16_t *buf);+int _hs_Text_ToShortest(double value, uint16_t *buf);+int _hs_ToShortest(double value, char *buf); int _hs_ToFixedLength(void);-int _hs_ToFixed(double value, uint16_t *buf, int ndigits);+int _hs_Text_ToFixed(double value, uint16_t *buf, int ndigits);+int _hs_ToFixed(double value, char *buf, int ndigits); int _hs_ToExponentialLength(void);-int _hs_ToExponential(double value, uint16_t *buf, int ndigits);+int _hs_Text_ToExponential(double value, uint16_t *buf, int ndigits);+int _hs_ToExponential(double value, char *buf, int ndigits); int _hs_ToPrecisionLength(void);-int _hs_ToPrecision(double value, uint16_t *buf, int ndigits);+int _hs_Text_ToPrecision(double value, uint16_t *buf, int ndigits);+int _hs_ToPrecision(double value, char *buf, int ndigits); #ifdef __cplusplus }