Skip to content

Commit e79e689

Browse files
added shortest format mode to RealFloat that prints the shortest possible string
1 parent 67c4cb4 commit e79e689

File tree

4 files changed

+44
-1
lines changed

4 files changed

+44
-1
lines changed

Data/ByteString/Builder/RealFloat.hs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ module Data.ByteString.Builder.RealFloat
7878
, standardDefaultPrecision
7979
, scientific
8080
, generic
81+
, shortest
8182
) where
8283

8384
import Data.ByteString.Builder.Internal (Builder)
8485
import qualified Data.ByteString.Builder.RealFloat.Internal as R
85-
import Data.ByteString.Builder.RealFloat.Internal (FloatFormat(..), fScientific, fGeneric)
86+
import Data.ByteString.Builder.RealFloat.Internal (FloatFormat(..), fScientific, fGeneric, fShortest, SpecialStrings(SpecialStrings), )
8687
import Data.ByteString.Builder.RealFloat.Internal (positiveZero, negativeZero)
8788
import qualified Data.ByteString.Builder.RealFloat.F2S as RF
8889
import qualified Data.ByteString.Builder.RealFloat.D2S as RD
@@ -162,6 +163,18 @@ standardSpecialStrings = scientificSpecialStrings
162163
generic :: FloatFormat
163164
generic = fGeneric 'e' Nothing (0,7) standardSpecialStrings
164165

166+
-- | Standard or scientific notation depending on which uses the least number of charabers.
167+
--
168+
-- @since ????
169+
shortest :: FloatFormat
170+
shortest = fShortest 'e' SpecialStrings
171+
{ nan = "NaN"
172+
, positiveInfinity = "Inf"
173+
, negativeInfinity = "-Inf"
174+
, positiveZero = "0"
175+
, negativeZero = "-0"
176+
}
177+
165178
-- TODO: support precision argument for FGeneric and FScientific
166179
-- | Returns a rendered Float. Returns the \'shortest\' representation in
167180
-- scientific notation and takes an optional precision argument in standard
@@ -251,6 +264,14 @@ formatFloating fmt f = case fmt of
251264
else sci eE
252265
FScientific {..} -> specialsOr specials $ sci eE
253266
FStandard {..} -> specialsOr specials $ std precision
267+
FShortest {..} -> specialsOr specials
268+
if e'' >= 0 && (olength + 2 >= e'' || olength == 1 && e'' <= 2)
269+
|| e'' < 0 && (olength + e'' >= (-3) || olength == 1 && e'' >= (-2))
270+
then std Nothing
271+
else sci eE
272+
where
273+
e'' = R.toInt e
274+
olength = R.decimalLength m
254275
where
255276
sci eE = BP.primBounded (R.toCharsScientific @a Proxy eE sign m e) ()
256277
std precision = printSign f <> showStandard (toWord64 m) e' precision

Data/ByteString/Builder/RealFloat/Internal.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ module Data.ByteString.Builder.RealFloat.Internal
8989
, FloatFormat(..)
9090
, fScientific
9191
, fGeneric
92+
, fShortest
9293

9394
, module Data.ByteString.Builder.RealFloat.TableGenerator
9495
) where
@@ -1001,6 +1002,10 @@ data FloatFormat
10011002
, stdExpoRange :: (Int, Int)
10021003
, specials :: SpecialStrings
10031004
}
1005+
| FShortest
1006+
{ eE :: Word8#
1007+
, specials :: SpecialStrings
1008+
}
10041009
deriving Show
10051010
fScientific :: Char -> SpecialStrings -> FloatFormat
10061011
fScientific eE specials = FScientific
@@ -1012,3 +1017,8 @@ fGeneric eE precision stdExpoRange specials = FGeneric
10121017
{ eE = asciiRaw $ ord eE
10131018
, ..
10141019
}
1020+
fShortest :: Char -> SpecialStrings -> FloatFormat
1021+
fShortest eE specials = FShortest
1022+
{ eE = asciiRaw $ ord eE
1023+
, ..
1024+
}

bytestring.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ test-suite bytestring-tests
181181
deepseq,
182182
ghc-prim,
183183
QuickCheck,
184+
quickcheck-assertions,
184185
tasty,
185186
tasty-hunit,
186187
tasty-quickcheck >= 0.8.1,

tests/builder/Data/ByteString/Builder/Tests.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import Test.Tasty.QuickCheck
6161
, (===), (.&&.), conjoin
6262
, UnicodeString(..), NonNegative(..)
6363
)
64+
import Test.QuickCheck.Assertions ((?<=))
6465
import QuickCheckUtils
6566

6667

@@ -743,6 +744,11 @@ testsFloating = testGroup "RealFloat"
743744
, ( 1.2345678 , "1.2345678" )
744745
, ( 1.23456735e-36 , "1.23456735e-36" )
745746
]
747+
, testProperty "shortest length always less than or equal to standard or scientific length outputs" \f -> let
748+
sh = L.length $ toLazyByteString $ formatFloat shortest f
749+
std = L.length $ toLazyByteString $ formatFloat standardDefaultPrecision f
750+
sci = L.length $ toLazyByteString $ formatFloat scientific f
751+
in sh ?<= min std sci
746752
, testMatches "f2sPowersOf10" floatDec show $
747753
fmap asShowRef [read ("1.0e" ++ show x) :: Float | x <- [-46..39 :: Int]]
748754
]
@@ -973,6 +979,11 @@ testsFloating = testGroup "RealFloat"
973979
, ( 549755813888.0e+3 , "5.49755813888e14" )
974980
, ( 8796093022208.0e+3 , "8.796093022208e15" )
975981
]
982+
, testProperty "shortest length always less than or equal to standard or scientific length outputs" \f -> let
983+
sh = L.length $ toLazyByteString $ formatDouble shortest f
984+
std = L.length $ toLazyByteString $ formatDouble standardDefaultPrecision f
985+
sci = L.length $ toLazyByteString $ formatDouble scientific f
986+
in sh ?<= min std sci
976987
, testMatches "d2sPowersOf10" doubleDec show $
977988
fmap asShowRef [read ("1.0e" ++ show x) :: Double | x <- [-324..309 :: Int]]
978989
]

0 commit comments

Comments
 (0)