diff --git a/formatn.cabal b/formatn.cabal
--- a/formatn.cabal
+++ b/formatn.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.4
 name:          formatn
-version:       0.1.0
+version:       0.2.0
 synopsis:      Formatting of doubles.
 description:
   This package provides support for number formatting styles, especially styles involving significant figure calculations.
@@ -37,9 +37,8 @@
     , base        >=4.7 && <5
     , containers  ^>=0.6
     , text        ^>=1.2
-
-  -- uncomment to run cabal-docspec --check-properties
-  -- QuickCheck,
+    -- just for cabal-docspec --check-properties
+    , QuickCheck
   exposed-modules:    Data.FormatN
   other-modules:
   default-language:   Haskell2010
diff --git a/src/Data/FormatN.hs b/src/Data/FormatN.hs
--- a/src/Data/FormatN.hs
+++ b/src/Data/FormatN.hs
@@ -2,6 +2,7 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE StrictData #-}
+{-# LANGUAGE TupleSections #-}
 {-# OPTIONS_GHC -Wall #-}
 
 -- | Text formatting of 'Double's.
@@ -22,32 +23,58 @@
 -- <https://hackage.haskell.org/package/rounded rounded> seems to be much more about doing computation taking rounding into account, compared with the much simpler task of pretty printing a number.
 --
 -- This library could have just provided an ability to compute a significant figure version of a number and then use these other libraries, but the round trip (from Double to SigFig to Double) introduces errors (eg the least significant figure goes from being a '4' to a '3999999' via float maths).
+--
+-- formatn is used in the <https://hackage.haskell.org/package/chart-svg chart-svg> library to automate consistent number formatting across different scales.
 module Data.FormatN
   ( -- * Usage
-    -- $usage
-
-    -- * FormatN
-    FormatN (..),
-    defaultFormatN,
-    formatN,
-    formatNs,
-    precision,
+    -- $setup
 
     -- * SigFig
     SigFig (..),
     SigFigSign (..),
     toSigFig,
     fromSigFig,
+    incSigFig,
 
-    -- * formatters
+    -- * specific formatters
+    FormatStyle (..),
+    precStyle,
+    commaPrecStyle,
+    FStyle (..),
+
+    -- * SigFig formatters
+    fixedSF,
+    exptSF,
+    exptSFWith,
+    decimalSF,
+    commaSF,
+    dollarSF,
+    percentSF,
+    formatSF,
+
+    -- * specific Double formatters
+    format,
+    formatOrShow,
     fixed,
     expt,
+    exptWith,
     decimal,
     prec,
     comma,
+    commaPrec,
     dollar,
     percent,
-    showOr,
+
+    -- * list modifiers
+    majorityStyle,
+    formats,
+    distinguish,
+
+    -- * FormatN
+    FormatN (..),
+    defaultFormatN,
+    formatN,
+    formatNs,
   )
 where
 
@@ -55,12 +82,15 @@
 import Data.Bool
 import Data.Containers.ListUtils (nubOrd)
 import Data.Foldable
+import qualified Data.List as List
+import qualified Data.Map.Strict as Map
 import Data.Maybe
+import Data.Ord
 import Data.Text (Text, pack)
 import qualified Data.Text as Text
 import GHC.Generics hiding (prec)
 import Numeric
-import Prelude
+import Prelude hiding (exponent)
 
 -- $setup
 -- >>> import Data.FormatN
@@ -72,101 +102,19 @@
 -- >>> decimal (Just 2) <$> xs
 -- ["-1.0","0.0","1.0","1.0","1.0","1.1","1.2"]
 --
--- >>> comma (Just 3) <$> xs
--- ["-1.00","0.00","1.00","1.01","1.02","1.10","1.20"]
---
--- comma (Just 3) . (1e3*) <$> xs
--- ["-1,000","0.00","1,000","1,010","1,020","1,100","1,200"]
---
--- comma (Just 3) . (1e-3*) <$> xs
+-- >>> decimal (Just 3) . (1e-3*) <$> xs
 -- ["-0.00100","0.00","0.00100","0.00101","0.00102","0.00110","0.00120"]
 --
--- >>> comma (Just 3) . (1e-6*) <$> xs
--- ["-1.00e-6","0.00","1.00e-6","1.01e-6","1.02e-6","1.10e-6","1.20e-6"]
---
--- >>> comma (Just 2) . (1e3*) <$> xs
--- ["-1,000","0.0","1,000","1,000","1,000","1,100","1,200"]
---
--- >>> precision comma (Just 2) $ (1e3*) <$> [0,1,1.01,1.02,1.1,1.2]
--- ["0.00","1,000","1,010","1,020","1,100","1,200"]
-
--- $usage
--- >>> import Data.FormatN
--- >>> xs = [(-1),0,1,1.01,1.02,1.1,1.2]
--- >>> fixed (Just 2) <$> xs
--- ["-1.00","0.00","1.00","1.01","1.02","1.10","1.20"]
---
--- >>> decimal (Just 2) <$> xs
--- ["-1.0","0.0","1.0","1.0","1.0","1.1","1.2"]
---
--- formatn is used in the <https://hackage.haskell.org/package/chart-svg chart-svg> library to automate consistent number formatting across different scales.
---
--- >>> comma (Just 3) <$> xs
--- ["-1.00","0.00","1.00","1.01","1.02","1.10","1.20"]
---
 -- >>> comma (Just 3) . (1e3*) <$> xs
 -- ["-1,000","0.00","1,000","1,010","1,020","1,100","1,200"]
 --
--- >>> comma (Just 3) . (1e-3*) <$> xs
--- ["-0.00100","0.00","0.00100","0.00101","0.00102","0.00110","0.00120"]
---
--- >>> comma (Just 3) . (1e-6*) <$> xs
--- ["-1.00e-6","0.00","1.00e-6","1.01e-6","1.02e-6","1.10e-6","1.20e-6"]
---
--- Using significant figures actually changes numbers - numbers that were slightly different end up being (and looking like) the same. 'precision' increases the number of significant figures to get around this.
---
--- >>> comma (Just 2) . (1e3*) <$> xs
--- ["-1,000","0.0","1,000","1,000","1,000","1,100","1,200"]
---
--- >>> precision comma (Just 2) $ (1e3*) <$> [0,1,1.01,1.02,1.1,1.2]
--- ["0.00","1,000","1,010","1,020","1,100","1,200"]
---
--- Also note the clunkiness of the treatment of zero. It is problematic to default format zero consistently.
-
--- | Wrapper for the various formatting options.
---
--- Nothing in the context of these constructors means do not perform and significant figure adjustments to the numbers (or decimal figures with respect to FormatFixed).
-data FormatN
-  = FormatFixed (Maybe Int)
-  | FormatDecimal (Maybe Int)
-  | FormatComma (Maybe Int)
-  | FormatExpt (Maybe Int)
-  | FormatPrec (Maybe Int)
-  | FormatDollar (Maybe Int)
-  | FormatPercent (Maybe Int)
-  | FormatNone
-  deriving (Eq, Show, Generic)
-
--- | The official format
---
--- >>> defaultFormatN
--- FormatComma (Just 2)
-defaultFormatN :: FormatN
-defaultFormatN = FormatComma (Just 2)
-
--- | run a 'FormatN'
---
--- >>> formatN defaultFormatN 1234
--- "1,200"
-formatN :: FormatN -> Double -> Text
-formatN (FormatFixed n) x = fixed n x
-formatN (FormatDecimal n) x = decimal n x
-formatN (FormatPrec n) x = prec n x
-formatN (FormatComma n) x = comma n x
-formatN (FormatExpt n) x = expt n x
-formatN (FormatDollar n) x = dollar n x
-formatN (FormatPercent n) x = percent n x
-formatN FormatNone x = pack (show x)
-
--- | Format to x decimal places with no significant figure rounding.
+-- Using significant figures actually changes numbers - numbers that were slightly different end up being (and looking like) the same. 'distinguish' increases the number of significant figures to get around this.
 --
--- >>> fixed (Just 2) 100
--- "100.00"
+-- >>> formats False (const CommaStyle) (Just 2) $ (1e3*) <$> xs
+-- ["-1,000","0","1,000","1,000","1,000","1,100","1,200"]
 --
--- >>> fixed (Just 2) 0.001
--- "0.00"
-fixed :: Maybe Int -> Double -> Text
-fixed n x = pack $ showFFloat n x ""
+-- >>> distinguish 4 False (const CommaStyle) (Just 2) $ (1e3*) <$> xs
+-- ["-1,000","0","1,000","1,010","1,020","1,100","1,200"]
 
 -- | Decomposition of a Double into the components that are needed to determine significant figure formatting.
 --
@@ -177,11 +125,11 @@
 -- \]
 data SigFig = SigFig
   { -- | sign
-    sign :: SigFigSign,
+    sfSign :: SigFigSign,
     -- | significant figures expressed as an Integer
-    figures :: Integer,
+    sfFigures :: Integer,
     -- | the power of 10 exponent given figures.
-    exponent :: Int
+    sfExponent :: Int
   }
   deriving (Eq, Show)
 
@@ -194,11 +142,11 @@
 -- | convert from a Double to a 'SigFig'
 --
 -- >>> toSigFig (Just 2) 1234
--- SigFig {sign = SigFigPos, figures = 12, exponent = 2}
+-- SigFig {sfSign = SigFigPos, sfFigures = 12, sfExponent = 2}
 --
 -- prop> \x -> let (SigFig s fs e) = toSigFig Nothing x in let x' = ((if (s==SigFigNeg) then (-1.0) else 1.0) * fromIntegral fs * 10.0**fromIntegral e) in (x==0 || abs (x/x'-1) < 1e-6)
 toSigFig :: Maybe Int -> Double -> SigFig
-toSigFig n x = SigFig s fs' expo
+toSigFig n x = SigFig s fs' expo'
   where
     (s, (floatfs, floate)) = bool (SigFigPos, floatToDigits 10 x) (SigFigNeg, floatToDigits 10 (-x)) (x < 0)
     -- floatToDigits 10 0 == ([0],0) floatToDigits 10 1 == ([1],1)
@@ -207,9 +155,9 @@
     -- pad with extra zeros if less figures than requested
     (floatfs', e) =
       bool
-      (floatfs, floate' - length floatfs)
-      (floatfs <> replicate (nsig - length floatfs) 0, floate' - nsig)
-      (length floatfs < nsig)
+        (floatfs, floate' - length floatfs)
+        (floatfs <> replicate (nsig - length floatfs) 0, floate' - nsig)
+        (length floatfs < nsig)
     (fs0, fs1) = splitAt nsig floatfs'
     -- reconstitute number to get rounding right at the least significance point
     fs =
@@ -219,21 +167,34 @@
     -- rounding can bump significant figures by 1 eg 99(.9999) ==> 100
     (fs', expo) =
       bool
-      (fs, e + length floatfs' - nsig)
-      (fs `div` 10, e + length floatfs' - nsig + 1)
-      (length (show fs) > nsig)
+        (fs, e + length floatfs' - nsig)
+        (fs `div` 10, e + length floatfs' - nsig + 1)
+        (length (show fs) > nsig)
+    -- zero fix
+    expo' = bool expo 0 (fs' == 0 && expo > 0)
 
 -- | convert from a 'SigFig' to a Double
 --
 -- >>> fromSigFig (SigFig SigFigPos 12 2)
 -- 1200.0
---
--- @fromSigFig . toSigFig Nothing@ may not be isomorphic
 fromSigFig :: SigFig -> Double
 fromSigFig (SigFig s fs e) = bool 1 (-1) (s == SigFigNeg) * fromIntegral fs * 10 ** fromIntegral e
 
-exptR :: SigFig -> Text
-exptR (SigFig s i e) = pack $ sfsign s <> i'' <> "e" <> show e'
+-- | increase significant figures
+incSigFig :: Int -> SigFig -> SigFig
+incSigFig n (SigFig s fs e) = SigFig s (fs * (10 ^ max 0 n)) (e - n)
+
+-- The natural exponent to format with
+eSF :: SigFig -> Int
+eSF (SigFig _ fs e) = e + length (show fs) - 1
+
+-- | expt format for a SigFig
+fixedSF :: Maybe Int -> SigFig -> Text
+fixedSF n sf = fixed n (fromSigFig sf)
+
+-- | expt format for a SigFig
+exptSF :: SigFig -> Text
+exptSF (SigFig s i e) = pack $ sfsign s <> i'' <> "e" <> show e'
   where
     i''
       | length i' == 1 = i'
@@ -241,31 +202,97 @@
     i' = show i
     e' = e + length i' - 1
 
--- | Format in exponential style, maybe with significant figure rounding.
+-- | expt format for a SigFig, with an exponent override
 --
--- >>> expt Nothing 1245
--- "1.245e3"
+-- >>> exptSFWith (Just 1) (toSigFig (Just 1) 1)
+-- "0.1e1"
 --
--- >>> expt (Just 3) 1245
--- "1.24e3"
+-- >>> exptSFWith (Just 0) (toSigFig (Just 1) 1)
+-- "1e0"
 --
--- >>> expt (Just 3) 0.1245
--- "1.24e-1"
-expt :: Maybe Int -> Double -> Text
-expt n x = exptR (toSigFig n x)
+-- >>> exptSFWith (Just (-1)) (toSigFig (Just 1) 1)
+-- "10e-1"
+exptSFWith :: Maybe Int -> SigFig -> Text
+exptSFWith eover (SigFig s i e) = pack (sfsign s) <> posDecimalSF i (e - e') <> "e" <> pack (show e')
+  where
+    e' = fromMaybe (e + length (show i) - 1) eover
 
-decimalR :: SigFig -> Text
-decimalR (SigFig s xs e) = pack $ sfsign s <> t
+-- Formatting the positive component in decimal style
+posDecimalSF :: Integer -> Int -> Text
+posDecimalSF xs e = pack t
   where
     xs' = show xs
     nsf = length xs'
     extrasf = bool (-(e + nsf)) (-(e + nsf)) (xs == 0)
     oversf = length xs' + e
     t
-      | e >= 0 = xs' <> replicate e '0'
+      | e >= 0 = bool (xs' <> replicate e '0') xs' (xs == 0)
       | e <= -nsf = "0." <> replicate extrasf '0' <> xs'
       | otherwise = take oversf xs' <> "." <> drop oversf xs'
 
+-- | comma format for a SigFig
+maybeCommaSF :: Bool -> SigFig -> Text
+maybeCommaSF doCommas (SigFig s xs e) = pack (sfsign s) <> bool id addcommas doCommas (posDecimalSF xs e)
+  where
+    addcommas =
+      uncurry (<>)
+        . first (Text.reverse . Text.intercalate "," . Text.chunksOf 3 . Text.reverse)
+        . Text.breakOn "."
+
+-- | comma format for a SigFig
+commaSF :: SigFig -> Text
+commaSF = maybeCommaSF True
+
+-- | decimal format for a SigFig
+decimalSF :: SigFig -> Text
+decimalSF = maybeCommaSF False
+
+-- | percent format for a SigFig
+percentSF :: (SigFig -> Text) -> SigFig -> Text
+percentSF f (SigFig s figs e) = (<> "%") $ f (SigFig s figs (e + 2))
+
+-- | dollar format for a SigFig
+dollarSF :: (SigFig -> Text) -> SigFig -> Text
+dollarSF f sf =
+  case sfSign sf of
+    SigFigNeg -> "-" <> dollarSF f (SigFig SigFigPos (sfFigures sf) (sfExponent sf))
+    SigFigPos -> "$" <> f sf
+
+-- * specific number formats
+
+-- | Format to x decimal places with no significant figure rounding.
+--
+-- >>> fixed (Just 2) 100
+-- "100.00"
+--
+-- >>> fixed (Just 2) 0.001
+-- "0.00"
+fixed :: Maybe Int -> Double -> Text
+fixed n x = pack $ showFFloat n x ""
+
+-- | Format in exponential style, maybe with significant figure rounding.
+--
+-- >>> expt Nothing 1245
+-- "1.245e3"
+--
+-- >>> expt (Just 3) 1245
+-- "1.24e3"
+--
+-- >>> expt (Just 3) 0.1245
+-- "1.24e-1"
+expt :: Maybe Int -> Double -> Text
+expt n x = exptSF (toSigFig n x)
+
+-- | Format in exponential style, with the suggested exponent.
+--
+-- >>> exptWith (Just 2) Nothing 1245
+-- "12.45e2"
+--
+-- >>> exptWith (Just 6) (Just 3) 1245
+-- "0.00124e6"
+exptWith :: Maybe Int -> Maybe Int -> Double -> Text
+exptWith n' n x = exptSFWith n' (toSigFig n x)
+
 -- | Format in decimal style, and maybe round to n significant figures.
 --
 -- >>> decimal Nothing 1.2345e-2
@@ -277,10 +304,139 @@
 -- >>> decimal (Just 2) 12345
 -- "12000"
 decimal :: Maybe Int -> Double -> Text
-decimal n x = decimalR (toSigFig n x)
+decimal n x = decimalSF (toSigFig n x)
 
--- | Format between 0.001 and 1,000,000 using decimal style and exponential style outside this range.
+-- | Format with US-style commas
 --
+-- >>> comma (Just 3) 1234567
+-- "1,230,000"
+comma :: Maybe Int -> Double -> Text
+comma n x = commaSF (toSigFig n x)
+
+-- | Adjust format to a percent.
+--
+-- >>> percent commaSF (Just 3) 0.1234
+-- "12.3%"
+--
+-- >>> percent decimalSF (Just 1) 0.1234
+-- "10%"
+percent :: (SigFig -> Text) -> Maybe Int -> Double -> Text
+percent f n x = percentSF f (toSigFig n x)
+
+-- | Adjust format to dollar style.
+--
+-- >>> dollar commaSF (Just 3) 1234
+-- "$1,230"
+--
+-- >>> dollar (fixedSF (Just 2)) (Just 2) 0.01234
+-- "$0.01"
+dollar :: (SigFig -> Text) -> Maybe Int -> Double -> Text
+dollar f n x = dollarSF f (toSigFig n x)
+
+-- | Data type representing styles of formatting
+data FormatStyle
+  = -- | 1000 1 0.001
+    DecimalStyle
+  | -- | The parameter represents the exponent to format to with Nothing meaning:
+    --
+    -- 1e3 1e1 1e-3
+    ExponentStyle (Maybe Int)
+  | -- | 1,000 1 0.001
+    CommaStyle
+  | -- | 1000.00 1.00 0.00
+    FixedStyle Int
+  | -- | 100,000% 100% 0.1%
+    PercentStyle
+  | -- | \$1,000 $1 $0.001
+    DollarStyle
+  deriving (Show, Eq, Ord)
+
+-- | DecimalStyle between 0.001 and 1000000 and ExponentStyle outside this range.
+precStyle :: Double -> FormatStyle
+precStyle x
+  | x == 0 = DecimalStyle
+  | abs x < 0.001 = ExponentStyle (Just (eSF (toSigFig Nothing x)))
+  | abs x > 1e6 = ExponentStyle (Just (eSF (toSigFig Nothing x)))
+  | otherwise = DecimalStyle
+
+-- | CommaStyle above 1,000 but below a million, DecimalStyle between 0.001 and a million and ExponentStyle outside this range.
+commaPrecStyle :: Double -> FormatStyle
+commaPrecStyle x
+  | x == 0 = CommaStyle
+  | abs x < 0.001 = ExponentStyle (Just (eSF (toSigFig Nothing x)))
+  | abs x > 1e6 = ExponentStyle (Just (eSF (toSigFig Nothing x)))
+  | otherwise = CommaStyle
+
+-- | Data type representing styles of formatting dependent on the number
+data FStyle
+  = FSDecimal
+  | FSExponent (Maybe Int)
+  | FSComma
+  | FSFixed Int
+  | FSPercent
+  | FSDollar
+  | FSPrec
+  | FSCommaPrec
+  | FSNone
+  deriving (Show, Eq, Ord)
+
+-- | Compute the majority FormatStyle so a list of numbers can all have the same formatting
+--
+-- Also equalises the exponent to majority for exponent style.
+--
+-- >>> commaPrecStyle <$> [0,5e6,1e7,2e7]
+-- [CommaStyle,ExponentStyle (Just 6),ExponentStyle (Just 7),ExponentStyle (Just 7)]
+--
+-- >>> majorityStyle commaPrecStyle [0,5e6,1e7,2e7]
+-- ExponentStyle (Just 7)
+majorityStyle :: (Double -> FormatStyle) -> [Double] -> FormatStyle
+majorityStyle s xs = maj'
+  where
+    maj = fromMaybe CommaStyle (major (neutralExpStyle . s <$> xs))
+    maj' = bool maj (ExponentStyle (fromMaybe Nothing expXs)) (maj == ExponentStyle Nothing)
+    neutralExpStyle (ExponentStyle _) = ExponentStyle Nothing
+    neutralExpStyle x = x
+
+    expXs = major [x | (ExponentStyle x) <- s <$> xs]
+
+major :: Ord a => [a] -> Maybe a
+major xs = fst <$> listToMaybe (List.sortOn (Down . snd) $ Map.toList $ Map.fromListWith (+) ((,1 :: Integer) <$> xs))
+
+-- | format a number according to a FormatStyle and significant figures
+--
+-- >>> format CommaStyle (Just 2) 1234
+-- "1,200"
+format :: FormatStyle -> Maybe Int -> Double -> Text
+format fs n x = bool (go x) ("-" <> go (-x)) (x < 0)
+  where
+    go x' = case fs of
+      DecimalStyle -> decimal n x'
+      ExponentStyle n' -> exptWith n' n x'
+      CommaStyle -> comma n x'
+      FixedStyle n' -> fixed (Just n') x'
+      PercentStyle -> percent commaSF n x'
+      DollarStyle -> dollar commaSF n x'
+
+-- | format a SigFig according to a style
+--
+-- >>> formatSF CommaStyle (toSigFig (Just 2) 1234)
+-- "1,200"
+--
+-- >>> formatSF CommaStyle (SigFig SigFigPos 0 1)
+-- "0"
+-- >>> formatSF CommaStyle (SigFig SigFigPos 0 (-1))
+-- "0.0"
+formatSF :: FormatStyle -> SigFig -> Text
+formatSF fs x = case fs of
+  DecimalStyle -> decimalSF x
+  ExponentStyle n' -> exptSFWith n' x
+  CommaStyle -> commaSF x
+  FixedStyle n -> fixed (Just n) (fromSigFig x)
+  PercentStyle -> percentSF commaSF x
+  DollarStyle -> dollarSF commaSF x
+
+-- | Format between 0.001 and 1000000 using decimal style and exponential style outside this range.
+--
 -- >>> prec (Just 2) 0.00234
 -- "0.0023"
 --
@@ -296,94 +452,144 @@
 -- >>> prec (Just 2) 1234567
 -- "1.2e6"
 prec :: Maybe Int -> Double -> Text
-prec n x = bool (go x) ("-" <> go (-x)) (x < 0)
-  where
-    go x' =
-      bool
-        ( bool
-            (decimal n x')
-            (expt n x')
-            (x' > 1e6)
-        )
-        (expt n x')
-        (x' < 0.001 && (x /= 0))
+prec n x = format (precStyle x) n x
 
 -- | Format using comma separators for numbers above 1,000 but below 1 million, otherwise use prec style.
 --
--- >>> comma (Just 3) 1234
+-- >>> commaPrec (Just 3) 1234
 -- "1,230"
-comma :: Maybe Int -> Double -> Text
-comma n x
-  | x < 0 = "-" <> comma n (-x)
-  | x < 1000 || x > 1e6 = prec n x
-  | otherwise = addcomma (prec n x)
-  where
-    addcomma =
-      uncurry (<>)
-        . first (Text.reverse . Text.intercalate "," . Text.chunksOf 3 . Text.reverse)
-        . Text.breakOn "."
+--
+-- >>> commaPrec (Just 3) 1234567
+-- "1.23e6"
+commaPrec :: Maybe Int -> Double -> Text
+commaPrec n x = format (commaPrecStyle x) n x
 
--- | Format as dollars, always using comma notation
+-- | Consistently format a list of numbers
 --
--- >>> dollar (Just 3) 1234
--- "$1,230"
+-- >>> formats True precStyle (Just 1) [0,0.5,1,2]
+-- ["0.0","0.5","1.0","2.0"]
 --
--- >>> dollar (Just 2) 0.01234
--- "$0.012"
-dollar :: Maybe Int -> Double -> Text
-dollar n x
-  | x < 0 = "-" <> dollar n (-x)
-  | otherwise = "$" <> comma n x
-
--- | Format as a percentage using decimal style.
+-- >>> formats False precStyle (Just 1) $ ((-1)*) <$> [0,0.5,1,2]
+-- ["0.0","-0.5","-1.0","-2.0"]
 --
--- >>> percent (Just 2) 0.001234
--- "0.12%"
-percent :: Maybe Int -> Double -> Text
-percent n x = (<> "%") $ decimal n (100 * x)
+-- >>> formats True commaPrecStyle (Just 1) $ ((-1000)*) <$> [0,0.5,1,2]
+-- ["     0","  -500","-1,000","-2,000"]
+--
+-- >>> formats True commaPrecStyle (Just 1) $ ((1e6)*) <$> [0,0.5,1,2]
+-- ["        0","  500,000","1,000,000","2,000,000"]
+--
+-- >>> formats True commaPrecStyle (Just 1) $ ((1e6)*) <$> [0.9,2,3]
+-- ["0.9e6","2.0e6","3.0e6"]
+--
+-- >>> formats True commaPrecStyle (Just 1) $ ((1e-6)*) <$> [0,0.5,1,2]
+-- ["0.0e-6","0.5e-6","1.0e-6","2.0e-6"]
+--
+-- >>> formats True commaPrecStyle (Just 1) $ ((1e-3)*) <$> [0,0.5,1,2]
+-- ["0.0000","0.0005","0.0010","0.0020"]
+formats ::
+  -- | left pad to the largest text length
+  Bool ->
+  -- | style
+  (Double -> FormatStyle) ->
+  -- | significant figures requested
+  Maybe Int ->
+  -- | list of numbers
+  [Double] ->
+  [Text]
+formats lpad s n0 xs = bool fsigs (lpads fsigs) lpad
+  where
+    sigs = toSigFig n0 <$> xs
+    minexp = minimum (sfExponent <$> filter (\x -> sfFigures x /= 0) sigs)
+    sigs' = (\x -> bool (incSigFig (sfExponent x - minexp) x) (x {sfExponent = min 1 minexp}) (sfFigures x == 0)) <$> sigs
+    maj = majorityStyle s (fromSigFig <$> sigs')
+    fsigs = formatSF maj <$> sigs'
 
-precision_ :: (Maybe Int -> Double -> Text) -> Int -> [Double] -> [Text]
-precision_ f n0 xs =
-  precLoop n0 xs
+lpads :: [Text] -> [Text]
+lpads ts = (\x -> mconcat (replicate (maxl - Text.length x) " ") <> x) <$> ts
   where
-    precLoop n xs' =
-      let s = f (Just n) <$> xs'
-       in bool (precLoop (1 + n) xs') s (s == nubOrd s || n > 4)
+    maxl = maximum $ Text.length <$> ts
 
 -- | Provide formatted text for a list of numbers so that they are just distinguished.
 --
--- For example, __@precision comma (Just 2)@__ means use as much significant figures as is needed for the numbers to be distinguished on rendering, but with at least 2 significant figures.
---
--- The difference between this and __@fmap (comma (Just 2))@__ can be seen in these examples:
+-- For example, __@distinguish 4 commaPrecStyle (Just 2)@__ means use as much significant figures as is needed for the numbers to be distinguished on rendering (up to 4+2=6), but with at least 2 significant figures.
 --
--- >>> precision comma (Just 2) [0,1,1.01,1.02,1.1,1.2]
--- ["0.00","1.00","1.01","1.02","1.10","1.20"]
+-- The difference between this and 'formats' can be seen in these examples:
 --
--- >>> fmap (comma (Just 2)) [0,1,1.01,1.02,1.1,1.2]
+-- >>> formats True commaPrecStyle (Just 2) [0,1,1.01,1.02,1.1,1.2]
 -- ["0.0","1.0","1.0","1.0","1.1","1.2"]
-precision :: (Maybe Int -> Double -> Text) -> Maybe Int -> [Double] -> [Text]
-precision f n xs =
+--
+-- >>> distinguish 4 True commaPrecStyle (Just 2) [0,1,1.01,1.02,1.1,1.2]
+-- ["0.00","1.00","1.01","1.02","1.10","1.20"]
+distinguish ::
+  -- | maximum number of iterations
+  Int ->
+  -- | left pad to the largest text length
+  Bool ->
+  -- | style
+  (Double -> FormatStyle) ->
+  -- | significant figures requested
+  Maybe Int ->
+  -- | list of numbers
+  [Double] ->
+  [Text]
+distinguish maxi pad f n xs =
   case n of
-    Nothing -> f Nothing <$> xs
-    Just n' -> precision_ f n' xs
+    Nothing -> formats pad f Nothing xs
+    Just n0 -> loop n0 xs
+  where
+    loop n' xs' =
+      let s = formats pad f (Just n') xs'
+       in bool (loop (1 + n') xs') s (s == nubOrd s || n' > maxi)
 
--- | Consistently format a list of numbers via using 'precision'.
+-- | Wrapper for the various formatting options.
 --
--- >>> formatNs defaultFormatN [0,1,1.01,1.02,1.1,1.2]
+-- >>> defaultFormatN
+-- FormatN {fstyle = FSCommaPrec, sigFigs = Just 2, addLPad = True}
+data FormatN = FormatN {fstyle :: FStyle, sigFigs :: Maybe Int, addLPad :: Bool} deriving (Eq, Show, Generic)
+
+-- | The official FormatN
+defaultFormatN :: FormatN
+defaultFormatN = FormatN FSCommaPrec (Just 2) True
+
+-- | run a 'FormatN'
+--
+-- >>> formatN defaultFormatN 1234
+-- "1,200"
+formatN :: FormatN -> Double -> Text
+formatN (FormatN FSDecimal sf _) x = format DecimalStyle sf x
+formatN (FormatN (FSExponent n) sf _) x = format (ExponentStyle n) sf x
+formatN (FormatN FSComma sf _) x = format CommaStyle sf x
+formatN (FormatN (FSFixed n) sf _) x = format (FixedStyle n) sf x
+formatN (FormatN FSPercent sf _) x = format PercentStyle sf x
+formatN (FormatN FSDollar sf _) x = format DollarStyle sf x
+formatN (FormatN FSPrec sf _) x = format (precStyle x) sf x
+formatN (FormatN FSCommaPrec sf _) x = format (commaPrecStyle x) sf x
+formatN (FormatN FSNone _ _) x = pack (show x)
+
+-- | Consistently format a list of numbers via using 'distinguish'.
+--
+-- >>> formatNs 4 defaultFormatN [0,1,1.01,1.02,1.1,1.2]
 -- ["0.00","1.00","1.01","1.02","1.10","1.20"]
-formatNs :: FormatN -> [Double] -> [Text]
-formatNs (FormatFixed n) xs = precision fixed n xs
-formatNs (FormatDecimal n) xs = precision decimal n xs
-formatNs (FormatPrec n) xs = precision prec n xs
-formatNs (FormatComma n) xs = precision comma n xs
-formatNs (FormatExpt n) xs = precision expt n xs
-formatNs (FormatDollar n) xs = precision dollar n xs
-formatNs (FormatPercent n) xs = precision percent n xs
-formatNs FormatNone xs = pack . show <$> xs
+formatNs :: Int -> FormatN -> [Double] -> [Text]
+formatNs maxi (FormatN FSDecimal sf pad) x = distinguish maxi pad (const DecimalStyle) sf x
+formatNs maxi (FormatN (FSExponent n) sf pad) x = distinguish maxi pad (const (ExponentStyle n)) sf x
+formatNs maxi (FormatN FSComma sf pad) x = distinguish maxi pad (const CommaStyle) sf x
+formatNs maxi (FormatN (FSFixed n) sf pad) x = distinguish maxi pad (const (FixedStyle n)) sf x
+formatNs maxi (FormatN FSPercent sf pad) x = distinguish maxi pad (const PercentStyle) sf x
+formatNs maxi (FormatN FSDollar sf pad) x = distinguish maxi pad (const DollarStyle) sf x
+formatNs maxi (FormatN FSPrec sf pad) x = distinguish maxi pad precStyle sf x
+formatNs maxi (FormatN FSCommaPrec sf pad) x = distinguish maxi pad commaPrecStyle sf x
+formatNs _ (FormatN FSNone _ pad) x = bool id lpads pad $ pack . show <$> x
 
--- | Format with the shorter of show and formatN.
-showOr :: FormatN -> Double -> Text
-showOr f x = bool (bool f' (pack s') (Text.length (pack s') < Text.length f')) "0" (x < 1e-6 && x > -1e-6)
+-- | Format with the shorter of show and a style.
+--
+-- >>> format (ExponentStyle Nothing) Nothing 0
+-- "0e0"
+--
+-- >>> formatOrShow (ExponentStyle Nothing) Nothing 0
+-- "0"
+formatOrShow :: FormatStyle -> Maybe Int -> Double -> Text
+formatOrShow f n x = bool (bool f' (pack s') (Text.length (pack s') < Text.length f')) "0" (x < 1e-6 && x > -1e-6)
   where
-    f' = formatN f x
+    f' = format f n x
     s' = show x
