diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
diff --git a/scientific-notation.cabal b/scientific-notation.cabal
--- a/scientific-notation.cabal
+++ b/scientific-notation.cabal
@@ -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
diff --git a/src/Data/Number/Scientific.hs b/src/Data/Number/Scientific.hs
--- a/src/Data/Number/Scientific.hs
+++ b/src/Data/Number/Scientific.hs
@@ -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
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -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"
