diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,14 @@
 # Revision history for cborg-json
 
-## 0.1.0.0  -- YYYY-mm-dd
+## 0.2.2.0  -- 2019-12-29
+
+* Use `replicateM` to significantly speed up decoding. Now faster than Aeson!
+
+* Add support for decoding half-precision and single-precision floating-point
+  numbers.
+
+* Version bumps and support for GHC 8.8
+
+## 0.1.0.0  -- 2017-08-01
 
 * First version. Released on an unsuspecting world.
diff --git a/bench/Main.hs b/bench/Main.hs
new file mode 100644
--- /dev/null
+++ b/bench/Main.hs
@@ -0,0 +1,67 @@
+module Main (main) where
+
+import qualified Data.ByteString.Lazy   as LBS
+import qualified Codec.Compression.GZip as GZip
+import           Control.Monad (unless)
+import           System.Directory (doesFileExist)
+import           System.Process   (callProcess)
+
+import           Codec.CBOR.JSON (encodeValue, decodeValue)
+import           Codec.CBOR.Read (deserialiseFromBytes)
+import           Codec.CBOR.Write (toLazyByteString, toStrictByteString)
+
+import           Criterion.Main
+                   ( defaultMain, Benchmark, bgroup, bench, env, nf, whnf )
+import qualified Data.Aeson as Aeson (Value, decode, encode)
+
+
+dataFile :: FilePath
+dataFile = "bench/data.json.gz"
+
+dataFileURL :: FilePath
+dataFileURL = "https://raw.githubusercontent.com/well-typed/cborg/master/cborg-json/bench/data.json.gz"
+
+fetchLargeJSON :: IO ()
+fetchLargeJSON = do
+    exists <- doesFileExist dataFile
+    unless exists $ do
+      putStrLn $ "Fetching bechmark data file from " ++ dataFileURL
+      callProcess "curl" [dataFileURL, "--output", dataFile]
+
+loadLargeJSONBytes :: IO LBS.ByteString
+loadLargeJSONBytes = do
+    fetchLargeJSON
+    fmap GZip.decompress (LBS.readFile dataFile)
+
+loadLargeJSON :: IO Aeson.Value
+loadLargeJSON = do
+    bytes <- loadLargeJSONBytes
+    let Just json = Aeson.decode bytes
+    return json
+
+loadLargeCBORBytes :: IO LBS.ByteString
+loadLargeCBORBytes =
+    fmap (toLazyByteString . encodeValue) loadLargeJSON
+
+main :: IO ()
+main = defaultMain benchmarks
+
+benchmarks :: [Benchmark]
+benchmarks =
+    [ bgroup "Decode"
+        [ env loadLargeJSONBytes $ \largeJSONBytes ->
+          bench "JSON" (whnf (Aeson.decode :: LBS.ByteString -> Maybe Aeson.Value) largeJSONBytes)
+
+        , env loadLargeCBORBytes $ \largeCBORBytes ->
+          bench "CBOR" (whnf (deserialiseFromBytes (decodeValue False)) largeCBORBytes)
+
+        , env loadLargeCBORBytes $ \largeCBORBytes ->
+          bench "CBOR lenient" (whnf (deserialiseFromBytes (decodeValue True)) largeCBORBytes)
+        ]
+    , env loadLargeJSON $ \largeJSON ->
+      bgroup "Encode"
+        [ bench "JSON" (nf Aeson.encode largeJSON)
+        , bench "CBOR" (nf (toLazyByteString . encodeValue) largeJSON)
+        , bench "CBOR strict" (whnf (toStrictByteString . encodeValue) largeJSON)
+        ]
+    ]
diff --git a/cborg-json.cabal b/cborg-json.cabal
--- a/cborg-json.cabal
+++ b/cborg-json.cabal
@@ -1,5 +1,5 @@
 name:                cborg-json
-version:             0.2.1.0
+version:             0.2.2.0
 synopsis:            A library for encoding JSON as CBOR
 description:         This package implements the bijection between JSON and
                      CBOR defined in the CBOR specification, RFC 7049.
@@ -16,12 +16,14 @@
 build-type:          Simple
 extra-source-files:  ChangeLog.md
 cabal-version:       >=1.10
+tested-with:
+  GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5, GHC==8.8.1
 
 library
   exposed-modules:     Codec.CBOR.JSON
   ghc-options:         -Wall
   build-depends:
-    base >=4.7 && <4.13,
+    base >=4.7 && < 4.14,
     aeson >=0.7 && <1.5,
     aeson-pretty >=0.8 && <0.9,
     unordered-containers >=0.2 && <0.3,
@@ -33,4 +35,29 @@
   default-language:    Haskell2010
 
   if impl(ghc >= 8.0)
-    ghc-options: -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances
+    ghc-options: -Wcompat -Wnoncanonical-monad-instances
+
+
+
+benchmark bench
+  type:              exitcode-stdio-1.0
+  hs-source-dirs:    bench
+  main-is:           Main.hs
+
+  default-language:  Haskell2010
+  ghc-options:
+    -Wall -rtsopts -fno-cse -fno-ignore-asserts -fno-warn-orphans -O2
+
+  other-modules:
+
+  build-depends:
+    base                    >= 4.6     && < 5.0,
+    cborg                                      ,
+    cborg-json                                 ,
+    aeson                                      ,
+    bytestring              >= 0.10.4  && < 0.11,
+    criterion               >= 1.0     && < 1.6,
+    deepseq                 >= 1.0     && < 1.5,
+    directory,
+    process,
+    zlib                    >= 0.5     && < 0.7
diff --git a/src/Codec/CBOR/JSON.hs b/src/Codec/CBOR/JSON.hs
--- a/src/Codec/CBOR/JSON.hs
+++ b/src/Codec/CBOR/JSON.hs
@@ -7,7 +7,7 @@
 
 import           Data.Monoid
 import           Control.Applicative
-import           Prelude
+import           Prelude hiding (decodeFloat)
 
 import           Codec.CBOR.Encoding
 import           Codec.CBOR.Decoding
@@ -49,13 +49,15 @@
       TypeNInt    -> decodeNumberIntegral
       TypeNInt64  -> decodeNumberIntegral
       TypeInteger -> decodeNumberIntegral
+      TypeFloat16 -> decodeNumberFloat16
+      TypeFloat32 -> decodeNumberFloating
       TypeFloat64 -> decodeNumberFloating
       TypeBool    -> Bool   <$> decodeBool
       TypeNull    -> Null   <$  decodeNull
       TypeString  -> String <$> decodeString
 
-      TypeListLen      -> decodeListLen >>= flip (decodeListN lenient) []
-      TypeListLenIndef -> decodeListLenIndef >> (decodeListIndef lenient) []
+      TypeListLen      -> decodeListLen >>= decodeListN lenient
+      TypeListLenIndef -> decodeListLenIndef >> decodeListIndef lenient []
       TypeMapLen       -> decodeMapLen >>= flip (decodeMapN lenient) HM.empty
 
       _           -> fail $ "unexpected CBOR token type for a JSON value: "
@@ -67,12 +69,17 @@
 decodeNumberFloating :: Decoder s Value
 decodeNumberFloating = Number . Scientific.fromFloatDigits <$> decodeDouble
 
-decodeListN :: Bool -> Int -> [Value] -> Decoder s Value
-decodeListN !lenient !n acc =
-    case n of
-      0 -> return $! Array (V.fromList (reverse acc))
-      _ -> do !t <- decodeValue lenient
-              decodeListN lenient (n-1) (t : acc)
+decodeNumberFloat16 :: Decoder s Value
+decodeNumberFloat16 = do
+    f <- decodeFloat
+    if isNaN f || isInfinite f
+        then return Null
+        else return $ Number (Scientific.fromFloatDigits f)
+
+decodeListN :: Bool -> Int -> Decoder s Value
+decodeListN !lenient !n = do
+  vec <- V.replicateM n (decodeValue lenient) 
+  return $! Array vec
 
 decodeListIndef :: Bool -> [Value] -> Decoder s Value
 decodeListIndef !lenient acc = do
