diff --git a/bench/bench.hs b/bench/bench.hs
--- a/bench/bench.hs
+++ b/bench/bench.hs
@@ -20,7 +20,9 @@
 main =
   defaultMain
       [ bench "double" $ nf (pd D.double) doublesC
-      , bench "rational" $ nf (pd (D.rational :: Decode' BS.ByteString Double)) doublesC
+      , bench "read double" $ nf (pd (D.read :: Decode' BS.ByteString Double)) doublesC
+      , bench "float" $ nf (pd D.float) doublesC
+      , bench "read float" $ nf (pd (D.read :: Decode' BS.ByteString Float)) doublesC
       ]
 
 doubles :: LBS.ByteString
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,9 @@
 # Revision history for sv
 
+## 1.3 -- 2019-01-14
+
+* Update to sv-core 0.4
+
 ## 1.2 -- 2018-09-26
 
 * Update to sv-core 0.3
diff --git a/sv.cabal b/sv.cabal
--- a/sv.cabal
+++ b/sv.cabal
@@ -1,5 +1,5 @@
 name:                sv
-version:             1.2
+version:             1.3
 license:             BSD3
 license-file:        LICENCE
 author:              George Wilson
@@ -76,8 +76,8 @@
 tested-with:         GHC == 7.10.3
                      , GHC == 8.0.2
                      , GHC == 8.2.2
-                     , GHC == 8.4.3
-                     , GHC == 8.6.1
+                     , GHC == 8.4.4
+                     , GHC == 8.6.3
 
 source-repository    head
   type:              git
@@ -91,14 +91,14 @@
                        , Data.Sv.Structure
   other-modules:       Data.Sv.Alien.Cassava
   -- other-extensions:    
-  build-depends:       base >=4.8 && <5
+  build-depends:       base >=4.8 && < 4.13
                        , attoparsec >= 0.12.1.4 && < 0.14
-                       , bifunctors >= 5.1 && < 6
+                       , bifunctors >= 5.1 && < 5.6
                        , bytestring >= 0.9.1.10 && < 0.11
                        , contravariant >= 1.2 && < 1.6
-                       , hw-dsv >= 0.2.1 && < 0.3
-                       , semigroupoids >= 5 && <6
-                       , sv-core >= 0.3 && < 0.4
+                       , hw-dsv >= 0.2.1 && < 0.4
+                       , semigroupoids >= 5 && < 5.4
+                       , sv-core >= 0.4 && < 0.5
                        , transformers >= 0.2 && < 0.6
                        , utf8-string >= 1 && < 1.1
                        , validation >= 1 && < 1.1
@@ -120,18 +120,18 @@
   default-language:
                        Haskell2010
   build-depends:
-                       base >=4.8 && <5
+                       base >=4.8 && < 4.13
                        , bytestring >= 0.9.1.10 && < 0.11
                        , cassava >= 0.4.1 && < 0.6
                        , contravariant >= 1.2 && < 1.6
                        , hedgehog >= 0.5 && < 0.7
-                       , lens >= 4 && < 5
+                       , lens >= 4 && < 4.18
                        , parsers >=0.12 && <0.13
                        , Only >= 0.1 && < 0.2
-                       , semigroupoids >= 5 && <6
+                       , semigroupoids >= 5 && < 5.4
                        , semigroups >= 0.18 && < 0.19
                        , sv
-                       , tasty >= 0.11 && < 1.2
+                       , tasty >= 0.11 && < 1.3
                        , tasty-hedgehog >= 0.1 && < 0.3
                        , tasty-hunit >= 0.9 && < 0.11
                        , text >= 1.0 && < 1.3
@@ -153,12 +153,12 @@
                        Haskell2010
   build-depends:
                          attoparsec >= 0.12.1.4 && < 0.14
-                       , base >=4.8 && <5
+                       , base >=4.8 && < 4.13
                        , bytestring >= 0.9.1.10 && < 0.11
                        , criterion >= 1.3 && < 1.6
                        , deepseq >= 1.1 && < 1.5
-                       , hw-dsv >= 0.2.1 && < 0.3
-                       , lens >= 4 && < 5
+                       , hw-dsv >= 0.2.1 && < 0.4
+                       , lens >= 4 && < 4.18
                        , sv
                        , text >= 1.0 && < 1.3
                        , vector >= 0.10 && < 0.13
diff --git a/test/Data/Sv/CassavaTest.hs b/test/Data/Sv/CassavaTest.hs
--- a/test/Data/Sv/CassavaTest.hs
+++ b/test/Data/Sv/CassavaTest.hs
@@ -4,13 +4,15 @@
 
 import qualified Data.ByteString as BS
 import qualified Data.Csv as Csv
-import Data.Sv (Validation (Failure, Success), Headedness (Unheaded), ParseOptions (_headedness), defaultParseOptions)
-import qualified Data.Sv as Sv (parseDecode)
+import Data.Sv (Validation (Failure, Success), Headedness (Unheaded), ParseOptions (_headedness), defaultParseOptions, defaultEncodeOptions)
+import qualified Data.Sv as Sv (parseDecode, encode)
 import Data.Sv.Decode (Decode')
 import qualified Data.Sv.Decode as Sv
+import Data.Sv.Encode (Encode)
+import qualified Data.Sv.Encode as E
 import Data.Vector as V
-import Data.Tuple.Only (Only (Only, fromOnly))
-import Hedgehog (Gen, (===), failure, forAll, property)
+import Data.Tuple.Only (Only (fromOnly))
+import Hedgehog (Gen, TestLimit, (===), failure, forAll, property, withTests)
 import qualified Hedgehog.Gen as Gen
 import qualified Hedgehog.Range as Range
 import Test.Tasty (TestName, TestTree, testGroup)
@@ -19,24 +21,26 @@
 test_CassavaAgreement :: TestTree
 test_CassavaAgreement =
   testGroup "cassava agreement"
-    [ cassavaAgreement "int" Sv.int (Gen.int (Range.linear (-10000000) 10000000))
-    , cassavaAgreement "char" Sv.char (Gen.unicode)
-    , cassavaAgreement "integer" Sv.integer (Gen.integral (Range.linear (-10000000) 10000000))
-    , cassavaAgreement "string" Sv.string (Gen.string (Range.linear 1 500) Gen.unicode)
-    , cassavaAgreement "bytestring" Sv.byteString (Gen.utf8 (Range.linear 1 500) Gen.unicode)
-    , cassavaAgreement "float" Sv.float (Gen.float (Range.exponentialFloat (-10000000) 10000000))
-    , cassavaAgreement "double" Sv.double (Gen.double (Range.exponentialFloat (-10000000) 10000000))
+    [ cassavaAgreement "int" Sv.int E.int (Gen.int (Range.linear (-10000000) 10000000))
+    , cassavaAgreement "char" Sv.char E.char (Gen.unicode)
+    , cassavaAgreement "integer" Sv.integer E.integer (Gen.integral (Range.linear (-10000000) 10000000))
+    , cassavaAgreement "string" Sv.string E.string (Gen.string (Range.linear 1 500) Gen.unicode)
+    , cassavaAgreement' "bytestring" 5000 Sv.byteString E.byteString (Gen.utf8 (Range.linear 1 600) Gen.unicode)
+    , cassavaAgreement' "float" 5000 Sv.float E.float (Gen.float (Range.exponentialFloat (-1000000000) 1000000000))
+    , cassavaAgreement' "double" 5000 Sv.double E.double (Gen.double (Range.exponentialFloat (-1000000000) 1000000000))
     ]
 
 opts :: ParseOptions
 opts = defaultParseOptions { _headedness = Unheaded }
 
 -- | Test that decoding with sv gets the same result as decoding with cassava
-cassavaAgreement :: forall a . (Csv.FromField a, Csv.ToField a, Show a, Eq a) => TestName -> Decode' BS.ByteString a -> Gen a -> TestTree
-cassavaAgreement name dec gen = testProperty name $ property $ do
+cassavaAgreement :: forall a . (Csv.FromField a, Csv.ToField a, Show a, Eq a) => TestName -> Decode' BS.ByteString a -> Encode a -> Gen a -> TestTree
+cassavaAgreement t = cassavaAgreement' t 100
+
+cassavaAgreement' :: forall a . (Csv.FromField a, Csv.ToField a, Show a, Eq a) => TestName -> TestLimit -> Decode' BS.ByteString a -> Encode a -> Gen a -> TestTree
+cassavaAgreement' name reps dec enc gen = testProperty name $ withTests reps $ property $ do
   a <- forAll gen
-  let oa = Only a
-  let sa = Csv.encode [oa]
+  let sa = Sv.encode enc defaultEncodeOptions [a]
   let cassava :: Either String [a]
       cassava = fmap fromOnly . V.toList <$> Csv.decode Csv.NoHeader sa
       sv = Sv.parseDecode dec opts sa
diff --git a/test/Data/Sv/DecodeTest.hs b/test/Data/Sv/DecodeTest.hs
--- a/test/Data/Sv/DecodeTest.hs
+++ b/test/Data/Sv/DecodeTest.hs
@@ -108,7 +108,7 @@
 data Semi = Semi Text Int Double Text deriving (Eq, Show)
 
 semiD :: D.Decode' ByteString Semi
-semiD = Semi <$> D.utf8 <*> (parseDecoder `o` D.contents) <*> D.rational <*> D.utf8
+semiD = Semi <$> D.utf8 <*> (parseDecoder `o` D.contents) <*> D.double <*> D.utf8
 
 semigroupoidTest :: TestTree
 semigroupoidTest = testGroup "Semigroupoid Decode"
@@ -120,7 +120,7 @@
         Failure (DecodeErrors (pure (BadDecode "no")))
   , testCase "Does the right thing in the case of right failure" $
       parseDecode semiD opts semiTestString3 @?=
-        Failure (DecodeErrors (pure (BadDecode "Couldn't decode \"false\": input does not start with a digit")))
+        Failure (DecodeErrors (pure (BadDecode "Couldn't decode \"false\" as a double")))
   ]
 
 -- This CSV has enough columns to make an Item, it has more columns than a
@@ -143,34 +143,34 @@
 inOrder :: NameDecode' ByteString Item
 inOrder =
   Item <$> D.column "id" D.int <*> D.column "name" D.utf8
-    <*> D.column "cost" D.rational <*> D.column "units" D.int
+    <*> D.column "cost" D.double <*> D.column "units" D.int
 
 outOrder :: NameDecode' ByteString Item
 outOrder =
   (\n u c i -> Item i n c u) <$> D.column "name" D.utf8
-    <*> D.column "units" D.int <*> D.column "cost" D.rational
+    <*> D.column "units" D.int <*> D.column "cost" D.double
     <*> D.column "id" D.int
 
 inOrderSemi :: NameDecode' ByteString SemiItem
 inOrderSemi =
-  SemiItem <$> D.column "name" D.utf8 <*> D.column "cost" D.rational
+  SemiItem <$> D.column "name" D.utf8 <*> D.column "cost" D.double
 
 outOrderSemi :: NameDecode' ByteString SemiItem2
 outOrderSemi =
-  SemiItem2 <$> D.column "units" D.int <*> D.column "cost" D.rational
+  SemiItem2 <$> D.column "units" D.int <*> D.column "cost" D.double
 
 super :: NameDecode' ByteString SuperItem
 super =
   SuperItem <$> D.column "id" D.int <*> D.column "name" D.utf8
     <*> D.column "manufacturer" D.utf8
-    <*> D.column "cost" D.rational <*> D.column "units" D.int
+    <*> D.column "cost" D.double <*> D.column "units" D.int
 
 super2 :: NameDecode' ByteString SuperItem2
 super2 =
   SuperItem2 <$> D.column "id" D.int <*> D.column "name" D.utf8
     <*> D.column "manufacturer" D.utf8
-    <*> D.column "cost" D.rational <*> D.column "units" D.int
-    <*> D.column "profit" D.rational
+    <*> D.column "cost" D.double <*> D.column "units" D.int
+    <*> D.column "profit" D.double
 
 namedTest :: TestTree
 namedTest = testGroup "Named decodes"
diff --git a/test/Data/Sv/EncodeTest.hs b/test/Data/Sv/EncodeTest.hs
--- a/test/Data/Sv/EncodeTest.hs
+++ b/test/Data/Sv/EncodeTest.hs
@@ -100,7 +100,7 @@
 three :: NameEncode Three
 three =
   E.named "first" (contramap int E.int)
-    <> E.named "\"Second\"" (contramap double E.double)
+    <> E.named "\"Second\"" (contramap double E.doubleFast)
     <> E.named "third" (contramap text E.text)
 
 myInt :: NameEncode Int
@@ -120,8 +120,8 @@
       encodeNamed three opts [] @?= "first,\"\"\"Second\"\"\",third"
   , testCase "multiple columns, one row" $
       encodeNamed three opts [Three 1 2 "th\"ree"]
-        @?= "first,\"\"\"Second\"\"\",third\n1,2.0,\"th\"\"ree\""
+        @?= "first,\"\"\"Second\"\"\",third\n1,2,\"th\"\"ree\""
   , testCase "multiple columns, multiple rows" $
       encodeNamed three opts [Three 1 2 "three", Three 4 5 "SIX"]
-        @?= "first,\"\"\"Second\"\"\",third\n1,2.0,three\n4,5.0,SIX"
+        @?= "first,\"\"\"Second\"\"\",third\n1,2,three\n4,5,SIX"
   ]
diff --git a/test/Data/Sv/RoundTripsDecodeEncode.hs b/test/Data/Sv/RoundTripsDecodeEncode.hs
--- a/test/Data/Sv/RoundTripsDecodeEncode.hs
+++ b/test/Data/Sv/RoundTripsDecodeEncode.hs
@@ -26,7 +26,8 @@
     , integer
     , float
     , double
-    , rational
+    , doubleFast
+    , readDouble
     , string
     , byteString
     , lazyByteString
@@ -113,13 +114,29 @@
 float = roundTripCodecIso "float" D.float E.float
   floatingTests
 
+doubleTests :: (IsString s, Fractional a) => [(s,a)]
+doubleTests = ("7.845860130857695", 7.845860130857695) : floatingTests
+
 double :: TestTree
 double = roundTripCodecIso "double" D.double E.double
-  floatingTests
+  ( ("1.0000000000034547e-2", 1.0000000000034547e-2)
+  : doubleTests
+  )
 
-rational :: TestTree
-rational = roundTripCodecIso "rational" D.rational E.double
-  (("7.845860130857695", 7.845860130857695) : floatingTests)
+doubleFast :: TestTree
+doubleFast = roundTripCodecIso "doubleFast" D.double E.doubleFast
+  [ ("5", 5)
+  , ("10.5", 10.5)
+  , ("12345.678", 12345.678)
+  , ("7.845860130857695", 7.845860130857695)
+  , ("0.010000000000034547", 1.0000000000034547e-2)
+  ]
+
+readDouble :: TestTree
+readDouble = roundTripCodecIso "read double" D.read (E.show :: Encode Double)
+  ( ("1.0000000000034547e-2", 1.0000000000034547e-2)
+  : doubleTests
+  )
 
 text :: TestTree
 text = roundTripCodecIso "text" D.utf8 E.text [(utf8lb "hello", "hello"), (utf8lb "💩💩💩💩", "💩💩💩💩")]
