diff --git a/Data/Double/Conversion.hs b/Data/Double/Conversion.hs
deleted file mode 100644
--- a/Data/Double/Conversion.hs
+++ /dev/null
@@ -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
diff --git a/Data/Double/Conversion/ByteString.hs b/Data/Double/Conversion/ByteString.hs
new file mode 100644
--- /dev/null
+++ b/Data/Double/Conversion/ByteString.hs
@@ -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)
diff --git a/Data/Double/Conversion/FFI.hs b/Data/Double/Conversion/FFI.hs
new file mode 100644
--- /dev/null
+++ b/Data/Double/Conversion/FFI.hs
@@ -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
diff --git a/Data/Double/Conversion/Text.hs b/Data/Double/Conversion/Text.hs
new file mode 100644
--- /dev/null
+++ b/Data/Double/Conversion/Text.hs
@@ -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)
diff --git a/benchmarks/Benchmarks.hs b/benchmarks/Benchmarks.hs
--- a/benchmarks/Benchmarks.hs
+++ b/benchmarks/Benchmarks.hs
@@ -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
diff --git a/benchmarks/double-conversion-benchmarks.cabal b/benchmarks/double-conversion-benchmarks.cabal
--- a/benchmarks/double-conversion-benchmarks.cabal
+++ b/benchmarks/double-conversion-benchmarks.cabal
@@ -10,6 +10,7 @@
 
   build-depends:
     base,
+    bytestring-show,
     criterion >= 0.5.0.10,
     double-conversion,
     text >= 0.11.0.8
diff --git a/cbits/hs-double-conversion.cc b/cbits/hs-double-conversion.cc
--- a/cbits/hs-double-conversion.cc
+++ b/cbits/hs-double-conversion.cc
@@ -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));
 }
diff --git a/double-conversion.cabal b/double-conversion.cabal
--- a/double-conversion.cabal
+++ b/double-conversion.cabal
@@ -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
 
diff --git a/include/hs-double-conversion.h b/include/hs-double-conversion.h
--- a/include/hs-double-conversion.h
+++ b/include/hs-double-conversion.h
@@ -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
 }
