packages feed

data-binary-ieee754 0.3 → 0.4

raw patch · 3 files changed

+406/−262 lines, 3 filesdep ~basedep ~binarydep ~bytestringPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, binary, bytestring

API changes (from Hackage documentation)

- Data.Binary.IEEE754: bias :: Exponent -> BitCount -> Exponent
- Data.Binary.IEEE754: bitSlice :: [Word8] -> BitCount -> BitCount -> Integer
- Data.Binary.IEEE754: data BitCount
- Data.Binary.IEEE754: data ByteCount
- Data.Binary.IEEE754: data Exponent
- Data.Binary.IEEE754: data Fraction
- Data.Binary.IEEE754: encodeIntBE :: ByteCount -> Integer -> [Word8]
- Data.Binary.IEEE754: encodeIntLE :: ByteCount -> Integer -> [Word8]
- Data.Binary.IEEE754: exponentWidth :: BitCount -> BitCount
- Data.Binary.IEEE754: floatComponents :: (RealFloat a) => ByteCount -> a -> (Bool, Fraction, Exponent)
- Data.Binary.IEEE754: floatToMerged :: (RealFloat a) => ByteCount -> a -> Integer
- Data.Binary.IEEE754: getFloat :: (RealFloat a) => ByteCount -> ([Word8] -> a) -> Get a
- Data.Binary.IEEE754: instance Bits Fraction
- Data.Binary.IEEE754: instance Enum Fraction
- Data.Binary.IEEE754: instance Eq Fraction
- Data.Binary.IEEE754: instance Integral Fraction
- Data.Binary.IEEE754: instance Num Fraction
- Data.Binary.IEEE754: instance Ord Fraction
- Data.Binary.IEEE754: instance Real Fraction
- Data.Binary.IEEE754: instance Show Fraction
- Data.Binary.IEEE754: mergeFloat :: Exponent -> Fraction -> BitCount -> (Integer, Int)
- Data.Binary.IEEE754: mergeFloatBits :: BitCount -> BitCount -> Bool -> Fraction -> Exponent -> Integer
- Data.Binary.IEEE754: parseFloatBE :: (RealFloat a) => [Word8] -> a
- Data.Binary.IEEE754: parseFloatLE :: (RealFloat a) => [Word8] -> a
- Data.Binary.IEEE754: putFloat :: (RealFloat a) => ByteCount -> (ByteCount -> Integer -> [Word8]) -> a -> Put
- Data.Binary.IEEE754: splitRawIEEE754 :: [Word8] -> (Bool, Exponent, Fraction)
- Data.Binary.IEEE754: unbias :: Exponent -> BitCount -> Exponent
+ Data.Binary.IEEE754: instance Bits Significand
+ Data.Binary.IEEE754: instance Enum Significand
+ Data.Binary.IEEE754: instance Eq Significand
+ Data.Binary.IEEE754: instance Integral Significand
+ Data.Binary.IEEE754: instance Num Significand
+ Data.Binary.IEEE754: instance Ord Significand
+ Data.Binary.IEEE754: instance Real Significand
+ Data.Binary.IEEE754: instance Show RawFloat
+ Data.Binary.IEEE754: instance Show Sign
+ Data.Binary.IEEE754: instance Show Significand

Files

− Data/Binary/IEEE754.hs
@@ -1,258 +0,0 @@-{- Copyright (C) 2009 John Millikin <jmillikin@gmail.com>-   -   This program is free software: you can redistribute it and/or modify-   it under the terms of the GNU General Public License as published by-   the Free Software Foundation, either version 3 of the License, or-   any later version.-   -   This program is distributed in the hope that it will be useful,-   but WITHOUT ANY WARRANTY; without even the implied warranty of-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-   GNU General Public License for more details.-   -   You should have received a copy of the GNU General Public License-   along with this program.  If not, see <http://www.gnu.org/licenses/>.--}--{-# LANGUAGE GeneralizedNewtypeDeriving #-}-module Data.Binary.IEEE754 (-	-- * Parsing-	 parseFloatBE, parseFloatLE-	-	,getFloat16be, getFloat16le-	,getFloat32be, getFloat32le-	,getFloat64be, getFloat64le-	-	,getFloat-	-	-- * Serializing-	,putFloat32be, putFloat32le-	,putFloat64be, putFloat64le-	-	,putFloat-	-	-- * Parser implementation-	,exponentWidth-	,bitSlice-	,splitRawIEEE754-	,unbias-	,mergeFloat-	-	-- * Serializer implementation-	,bias-	,encodeIntBE, encodeIntLE-	,floatToMerged-	,mergeFloatBits-	,floatComponents-	-	-- * Useful type aliases-	,Exponent-	,Fraction-	,BitCount-	,ByteCount-) where--import Data.Bits ((.&.), (.|.), shiftL, shiftR, Bits)-import Data.Word (Word8)-import Data.List (foldl')--import qualified Data.ByteString as B-import Data.Binary.Get (Get, getByteString)-import Data.Binary.Put (Put, putByteString)--------------------------------------------------------------------------- |Parse a big-endian byte list into a floating-point value.-parseFloatBE :: (RealFloat a) => [Word8] -> a-parseFloatBE = parseFloat---- |Parse a little-endian byte list into a floating-point value.-parseFloatLE :: (RealFloat a) => [Word8] -> a-parseFloatLE = parseFloat . reverse--getFloat16be :: Get Float-getFloat16be = getFloat (ByteCount 2) parseFloatBE--getFloat16le :: Get Float-getFloat16le = getFloat (ByteCount 2) parseFloatLE--getFloat32be :: Get Float-getFloat32be = getFloat (ByteCount 4) parseFloatBE--getFloat32le :: Get Float-getFloat32le = getFloat (ByteCount 4) parseFloatLE--getFloat64be :: Get Double-getFloat64be = getFloat (ByteCount 8) parseFloatBE--getFloat64le :: Get Double-getFloat64le = getFloat (ByteCount 8) parseFloatLE---- |Parse a floating-point value of the given width (in bytes) from within--- a Get monad.-getFloat :: (RealFloat a) => ByteCount -> ([Word8] -> a) -> Get a-getFloat (ByteCount width) parser = do-	bytes <- getByteString width-	(return . parser . B.unpack) bytes-------------------------------------------------------------------------putFloat32be :: Float -> Put-putFloat32be x = putFloat (ByteCount 4) encodeIntBE x--putFloat32le :: Float -> Put-putFloat32le x = putFloat (ByteCount 4) encodeIntLE x--putFloat64be :: Double -> Put-putFloat64be x = putFloat (ByteCount 8) encodeIntBE x--putFloat64le :: Double -> Put-putFloat64le x = putFloat (ByteCount 8) encodeIntLE x--putFloat :: (RealFloat a) => ByteCount -> (ByteCount -> Integer -> [Word8]) -> a -> Put-putFloat width f v = putByteString $ B.pack words'-	where words' = f width (floatToMerged width v)--floatComponents :: (RealFloat a) => ByteCount -> a -> (Bool, Fraction, Exponent)-floatComponents width v =-	case (dFraction, dExponent, biasedE) of-		(0, 0, _) -> (sign, 0, 0)-		(_, _, 0) -> (sign, truncatedFraction + 1, 0)-		_         -> (sign, truncatedFraction, biasedE)-	where dFraction   = Fraction $ fst (decodeFloat v)-	      dExponent   = Exponent $ snd (decodeFloat v)-	      eWidth      = exponentWidth (bitCount width)-	      fWidth      = (bitCount width) - eWidth - 1 -- 1 for sign bit-	      biasedE     = bias (dExponent + (fromIntegral fWidth)) eWidth-	      absFraction = abs dFraction-	-	      -- Weird check is for detecting -0.0-	      sign        = (1.0 / v) < 0.0-	-	      -- Fraction needs to be truncated, depending on the exponent-	      truncatedFraction = absFraction - (1 `bitShiftL` fWidth)--floatToMerged :: (RealFloat a) => ByteCount -> a -> Integer-floatToMerged width v = mergeFloatBits' (floatComponents width v)-	where mergeFloatBits' (s, f, e) = mergeFloatBits fWidth eWidth s f e-	      eWidth      = exponentWidth (bitCount width)-	      fWidth      = (bitCount width) - eWidth - 1 -- 1 for sign bit--mergeFloatBits :: BitCount -> BitCount -> Bool -> Fraction -> Exponent -> Integer-mergeFloatBits fWidth eWidth s f e = shiftedSign .|. shiftedFrac .|. shiftedExp-	where sBit = (if s then 1 else 0) :: Integer-	      shiftedSign = (sBit `bitShiftL` (fWidth + eWidth)) :: Integer-	      shiftedExp  = ((fromIntegral e) `bitShiftL` fWidth) :: Integer-	      shiftedFrac = fromIntegral f---- |Encode an integer to a list of words, in big-endian format-encodeIntBE :: ByteCount -> Integer -> [Word8]-encodeIntBE 0     _ = []-encodeIntBE width x = (encodeIntBE (width - 1) (x `shiftR` 8)) ++ [step]-	where step = (fromIntegral x) .&. 0xFF---- |Encode an integer to a list of words, in little-endian format-encodeIntLE :: ByteCount -> Integer -> [Word8]-encodeIntLE width x = reverse (encodeIntBE width x)--bias :: Exponent -> BitCount -> Exponent-bias e eWidth = e - (1 - (2 `iExp` (eWidth - 1)))-------------------------------------------------------------------------parseFloat :: (RealFloat a) => [Word8] -> a-parseFloat bs = merge' (splitRawIEEE754 bs)-	where merge'  (sign, e, f) = encode' (mergeFloat e f width) * signFactor sign-	      encode' (f, e)       = encodeFloat f e-	      signFactor s         = if s then (-1) else 1-	      width                = bitsInWord8 bs---- |Considering a byte list as a sequence of bits, slice it from start--- inclusive to end exclusive, and return the resulting bit sequence as an--- integer-bitSlice :: [Word8] -> BitCount -> BitCount -> Integer-bitSlice bs = sliceInt (foldl' step 0 bs) bitCount'-	where step acc w     = (shiftL acc 8) + (fromIntegral w)-	      bitCount'      = bitsInWord8 bs---- |Slice a single integer by start and end bit location-sliceInt :: Integer -> BitCount -> BitCount -> BitCount -> Integer-sliceInt x xBitCount s e = fromIntegral $ (x .&. startMask) `bitShiftR` (xBitCount - e)-	where startMask = n1Bits (xBitCount - s)-	      n1Bits n  = (2 `iExp` n) - 1---- |Split a raw bit array into (sign, exponent, fraction) components. These--- components have not been processed (unbiased, added significant bit,--- etc).-splitRawIEEE754 :: [Word8] -> (Bool, Exponent, Fraction)-splitRawIEEE754 bs = (sign, exp', frac)-	where sign = (head bs .&. 0x80) == 0x80-	      exp' = Exponent (fromIntegral $ bitSlice bs 1 (1 + w))-	      frac = Fraction (bitSlice bs (1 + w) (bitsInWord8 bs))-	      w    = exponentWidth $ bitsInWord8 bs---- |Unbias an exponent-unbias :: Exponent -> BitCount -> Exponent-unbias e eWidth = e + 1 - (2 `iExp` (eWidth - 1))---- |Parse values into a form suitable for encodeFloat--- sign exponent fraction width-in-bits -> fraction, exponent-mergeFloat :: Exponent -> Fraction -> BitCount -> (Integer, Int)---- Zero-mergeFloat 0 0 _ = (0, 0)--mergeFloat e f width-	-- Infinity / NaN (TODO)-	| e == eMax = error "Infinity/NaN not supported"-	-	| otherwise = case e of-		-- Denormalized-		0 -> (fromIntegral f, (fromIntegral unbiasedE + 1) - (fromIntegral fWidth))-		-		-- Normalized-		_ -> (fromIntegral f + (1 `bitShiftL` fWidth), (fromIntegral unbiasedE) - (fromIntegral fWidth))-		-		where eWidth    = exponentWidth width-		      fWidth    = width - eWidth - 1-		      eMax      = (2 `iExp` eWidth) - 1-		      unbiasedE = unbias e (eWidth)--------------------------------------------------------------------------- |Calculate the proper size of the exponent field, in bits, given the--- size of the full structure.-exponentWidth :: BitCount -> BitCount-exponentWidth k-	| k == 16         = 5-	| k == 32         = 8-	| k `mod` 32 == 0 = ceiling (4 * (logBase 2 (fromIntegral k))) - 13-	| otherwise       = error "Invalid length of floating-point value"---- |Integral exponent-iExp :: (Integral a, Integral b, Integral c) => a -> b -> c-iExp b e = floor $ (fromIntegral b) ** (fromIntegral e)--newtype Exponent = Exponent Int-	deriving (Show, Eq, Num, Ord, Real, Enum, Integral, Bits)--newtype Fraction = Fraction Integer-	deriving (Show, Eq, Num, Ord, Real, Enum, Integral, Bits)--newtype BitCount = BitCount Int-	deriving (Show, Eq, Num, Ord, Real, Enum, Integral)--newtype ByteCount = ByteCount Int-	deriving (Show, Eq, Num, Ord, Real, Enum, Integral)--bitCount :: ByteCount -> BitCount-bitCount (ByteCount x) = BitCount (x * 8)--bitsInWord8 :: [Word8] -> BitCount-bitsInWord8 ws = bitCount (ByteCount (length ws))--bitShiftL :: (Bits a) => a -> BitCount -> a-bitShiftL x (BitCount n) = shiftL x n--bitShiftR :: (Bits a) => a -> BitCount -> a-bitShiftR x (BitCount n) = shiftR x n
+ Data/Binary/IEEE754.lhs view
@@ -0,0 +1,402 @@+% Copyright (C) 2009 John Millikin <jmillikin@gmail.com>+% +% This program is free software: you can redistribute it and/or modify+% it under the terms of the GNU General Public License as published by+% the Free Software Foundation, either version 3 of the License, or+% any later version.+% +% This program is distributed in the hope that it will be useful,+% but WITHOUT ANY WARRANTY; without even the implied warranty of+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+% GNU General Public License for more details.+% +% You should have received a copy of the GNU General Public License+% along with this program.  If not, see <http://www.gnu.org/licenses/>.++\ignore{+\begin{code}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Data.Binary.IEEE754 (+	-- * Parsing+	  getFloat16be, getFloat16le+	, getFloat32be, getFloat32le+	, getFloat64be, getFloat64le+	+	-- * Serializing+	, putFloat32be, putFloat32le+	, putFloat64be, putFloat64le+) where++import Data.Bits ((.&.), (.|.), shiftL, shiftR, Bits)+import Data.Word (Word8)+import Data.List (foldl')++import qualified Data.ByteString as B+import Data.Binary.Get (Get, getByteString)+import Data.Binary.Put (Put, putByteString)+\end{code}+}++\section{Parsing}++\subsection{Public interface}++\begin{code}+getFloat16be :: Get Float+getFloat16be = getFloat (ByteCount 2) splitBytes+\end{code}++\begin{code}+getFloat16le :: Get Float+getFloat16le = getFloat (ByteCount 2) $ splitBytes . reverse+\end{code}++\begin{code}+getFloat32be :: Get Float+getFloat32be = getFloat (ByteCount 4) splitBytes+\end{code}++\begin{code}+getFloat32le :: Get Float+getFloat32le = getFloat (ByteCount 4) $ splitBytes . reverse+\end{code}++\begin{code}+getFloat64be :: Get Double+getFloat64be = getFloat (ByteCount 8) splitBytes+\end{code}++\begin{code}+getFloat64le :: Get Double+getFloat64le = getFloat (ByteCount 8) $ splitBytes . reverse+\end{code}++\subsection{Implementation}++Split the raw byte array into (sign, exponent, significand) components.+The exponent and signifcand are drawn directly from the bits in the+original float, and have not been unbiased or otherwise modified.++\begin{code}+splitBytes :: [Word8] -> RawFloat+splitBytes bs = RawFloat width sign exp' sig expWidth sigWidth where+	width = ByteCount (length bs)+	nBits = bitsInWord8 bs+	sign = if head bs .&. 0x80 == 0x80+		then Negative+		else Positive+	+	expStart = 1+	expWidth = exponentWidth nBits+	expEnd = expStart + expWidth+	exp' = Exponent . fromIntegral $ bitSlice bs expStart expEnd+	+	sigWidth = nBits - expEnd+	sig  = Significand $ bitSlice bs expEnd nBits+\end{code}++\subsubsection{Encodings and special values}++The next step depends on the value of the exponent $e$, size of the+exponent field in bits $w$, and value of the significand.++\begin{table}[h]+\begin{center}+\begin{tabular}{lrl}+\toprule+Exponent                & Significand & Format \\+\midrule+$0$                     & $0$           & Zero \\+$0$                     & $> 0$         & Denormalised \\+$1 \leq e \leq 2^w - 2$ & \textit{any} & Normalised \\+$2^w-1$                 & $0$           & Infinity \\+$2^w-1$                 & $> 0$         & NaN \\+\bottomrule+\end{tabular}+\end{center}+\end{table}++There's no built-in literals for Infinity or NaN, so they+are constructed using the {\tt Read} instances for {\tt Double} and+{\tt Float}.++\begin{code}+merge :: (Read a, RealFloat a) => RawFloat -> a+merge f@(RawFloat _ _ e sig eWidth _)+	| e == 0 = if sig == 0+		then 0.0+		else denormalised f+	| e == eMax - 1 = if sig == 0+		then read "Infinity"+		else read "NaN"+	| otherwise = normalised f+	where eMax = 2 `pow` eWidth+\end{code}++If a value is normalised, its significand has an implied {\tt 1} bit+in its most-significant bit. The significand must be adjusted by+this value before being passed to {\tt encodeField}.++\begin{code}+normalised :: RealFloat a => RawFloat -> a+normalised f = encodeFloat fraction exp' where+	Significand sig = rawSignificand f+	Exponent exp' = unbiased - sigWidth+	+	fraction = sig + (1 `bitShiftL` rawSignificandWidth f)+	+	sigWidth = fromIntegral $ rawSignificandWidth f+	unbiased = unbias (rawExponent f) (rawExponentWidth f)+\end{code}++For denormalised values, the implied {\tt 1} bit is the least-significant+bit of the exponent.++\begin{code}+denormalised :: RealFloat a => RawFloat -> a+denormalised f = encodeFloat sig exp' where+	Significand sig = rawSignificand f+	Exponent exp' = unbiased - sigWidth + 1+	+	sigWidth = fromIntegral $ rawSignificandWidth f+	unbiased = unbias (rawExponent f) (rawExponentWidth f)+\end{code}++By composing {\tt splitBytes} and {\tt merge}, the absolute value of the+float is calculated. Before being returned to the calling function, it+must be signed appropriately.++\begin{code}+getFloat :: (Read a, RealFloat a) => ByteCount+            -> ([Word8] -> RawFloat) -> Get a+getFloat (ByteCount width) parser = do+	raw <- fmap (parser . B.unpack) $ getByteString width+	let absFloat = merge raw+	return $ case rawSign raw of+		Positive ->  absFloat+		Negative -> -absFloat+\end{code}++\section{Serialising}++\subsection{Public interface}++\begin{code}+putFloat32be :: Float -> Put+putFloat32be = putFloat (ByteCount 4) id+\end{code}++\begin{code}+putFloat32le :: Float -> Put+putFloat32le = putFloat (ByteCount 4) reverse+\end{code}++\begin{code}+putFloat64be :: Double -> Put+putFloat64be = putFloat (ByteCount 8) id+\end{code}++\begin{code}+putFloat64le :: Double -> Put+putFloat64le = putFloat (ByteCount 8) reverse+\end{code}++\subsection{Implementation}++Serialisation is similar to parsing. First, the float is converted to+a structure representing raw bitfields. The values returned from+{\tt decodeFloat} are clamped to their expected lengths before being+stored in the {\tt RawFloat}.++\begin{code}+splitFloat :: RealFloat a => ByteCount -> a -> RawFloat+splitFloat width x = raw where+	raw = RawFloat width sign clampedExp clampedSig expWidth sigWidth+	sign = if isNegativeNaN x || isNegativeZero x || x < 0+		then Negative+		else Positive+	clampedExp = clamp expWidth exp'+	clampedSig = clamp sigWidth sig+	(exp', sig) = case (dFraction, dExponent, biasedExp) of+		(0, 0, _) -> (0, 0)+		(_, _, 0) -> (0, Significand $ truncatedSig + 1)+		_         -> (biasedExp, Significand truncatedSig)+	expWidth = exponentWidth $ bitCount width+	sigWidth = bitCount width - expWidth - 1 -- 1 for sign bit+	+	(dFraction, dExponent) = decodeFloat x+	+	rawExp = Exponent $ dExponent + fromIntegral sigWidth+	biasedExp = bias rawExp expWidth+	truncatedSig = abs dFraction - (1 `bitShiftL` sigWidth)+\end{code}++Then, the {\tt RawFloat} is converted to a list of bytes by mashing all+the fields together into an {\tt Integer}, and chopping up that integer+in 8-bit blocks.++\begin{code}+rawToBytes :: RawFloat -> [Word8]+rawToBytes raw = integerToBytes mashed width where+	RawFloat width sign exp' sig expWidth sigWidth = raw+	sign' :: Word8+	sign' = case sign of+		Positive -> 0+		Negative -> 1+	mashed = mashBits sig sigWidth .+	         mashBits exp' expWidth .+	         mashBits sign' 1 $ 0+\end{code}++{\tt clamp}, given a maximum bit count and a value, will strip any 1-bits+in positions above the count.++\begin{code}+clamp :: Bits a => BitCount -> a -> a+clamp = (.&.) . mask where+	mask 1 = 1+	mask n | n > 1 = (mask (n - 1) `shiftL` 1) + 1+	mask _ = undefined+\end{code}++For merging the fields, just shift the starting integer over a bit and+then \textsc{or} it with the next value. The weird parameter order allows+easy composition.++\begin{code}+mashBits :: (Bits a, Integral a) => a -> BitCount -> Integer -> Integer+mashBits _ 0 x = x+mashBits y n x = (x `bitShiftL` n) .|. fromIntegral y+\end{code}++Given an integer, read it in 255-block increments starting from the LSB.+Each increment is converted to a byte and added to the final list.++\begin{code}+integerToBytes :: Integer -> ByteCount -> [Word8]+integerToBytes _ 0 = []+integerToBytes x n = bytes where+	bytes = integerToBytes (x `shiftR` 8) (n - 1) ++ [step]+	step = fromIntegral x .&. 0xFF+\end{code}++Finally, the raw parsing is wrapped up in {\tt Put}. The second parameter+allows the same code paths to be used for little- and big-endian+serialisation.++\begin{code}+putFloat :: (RealFloat a) => ByteCount -> ([Word8] -> [Word8]) -> a -> Put+putFloat width f x = putByteString $ B.pack bytes where+	bytes = f . rawToBytes . splitFloat width $ x+\end{code}++\section{Raw float components}++Information about the raw bit patterns in the float is stored in+{\tt RawFloat}, so they don't have to be passed around to the various+format cases. The exponent should be biased, and the significand+shouldn't have it's implied MSB (if applicable).++\begin{code}+data RawFloat = RawFloat+	{ rawWidth            :: ByteCount+	, rawSign             :: Sign+	, rawExponent         :: Exponent+	, rawSignificand      :: Significand+	, rawExponentWidth    :: BitCount+	, rawSignificandWidth :: BitCount+	}+	deriving (Show)+\end{code}++\section{Exponents}++Calculate the proper size of the exponent field, in bits, given the+size of the full structure.++\begin{code}+exponentWidth :: BitCount -> BitCount+exponentWidth k+	| k == 16         = 5+	| k == 32         = 8+	| k `mod` 32 == 0 = ceiling (4 * logBase 2 (fromIntegral k)) - 13+	| otherwise       = error "Invalid length of floating-point value"+\end{code}++\begin{code}+bias :: Exponent -> BitCount -> Exponent+bias e eWidth = e - (1 - (2 `pow` (eWidth - 1)))+\end{code}++\begin{code}+unbias :: Exponent -> BitCount -> Exponent+unbias e eWidth = e + 1 - (2 `pow` (eWidth - 1))+\end{code}++\section{Byte and bit counting}++\begin{code}+data Sign = Positive | Negative+	deriving (Show)++newtype Exponent = Exponent Int+	deriving (Show, Eq, Num, Ord, Real, Enum, Integral, Bits)++newtype Significand = Significand Integer+	deriving (Show, Eq, Num, Ord, Real, Enum, Integral, Bits)++newtype BitCount = BitCount Int+	deriving (Show, Eq, Num, Ord, Real, Enum, Integral)++newtype ByteCount = ByteCount Int+	deriving (Show, Eq, Num, Ord, Real, Enum, Integral)++bitCount :: ByteCount -> BitCount+bitCount (ByteCount x) = BitCount (x * 8)++bitsInWord8 :: [Word8] -> BitCount+bitsInWord8 = bitCount . ByteCount . length++bitShiftL :: (Bits a) => a -> BitCount -> a+bitShiftL x (BitCount n) = shiftL x n++bitShiftR :: (Bits a) => a -> BitCount -> a+bitShiftR x (BitCount n) = shiftR x n+\end{code}++\section{Utility}++Considering a byte list as a sequence of bits, slice it from start+inclusive to end exclusive, and return the resulting bit sequence as an+integer.++\begin{code}+bitSlice :: [Word8] -> BitCount -> BitCount -> Integer+bitSlice bs = sliceInt (foldl' step 0 bs) bitCount' where+	step acc w     = shiftL acc 8 + fromIntegral w+	bitCount'      = bitsInWord8 bs+\end{code}++Slice a single integer by start and end bit location++\begin{code}+sliceInt :: Integer -> BitCount -> BitCount -> BitCount -> Integer+sliceInt x xBitCount s e = fromIntegral sliced where+	sliced = (x .&. startMask) `bitShiftR` (xBitCount - e)+	startMask = n1Bits (xBitCount - s)+	n1Bits n  = (2 `pow` n) - 1+\end{code}++Integral version of {\tt (**)}++\begin{code}+pow :: (Integral a, Integral b, Integral c) => a -> b -> c+pow b e = floor $ fromIntegral b ** fromIntegral e+\end{code}++Detect whether a float is {\tt $-$NaN}++\begin{code}+isNegativeNaN :: RealFloat a => a -> Bool+isNegativeNaN x = isNaN x && (floor x > 0)+\end{code}
data-binary-ieee754.cabal view
@@ -1,6 +1,6 @@ name: data-binary-ieee754-version: 0.3-synopsis: Parser/Serializer for IEEE-754 floating-point values+version: 0.4+synopsis: Parser/Serialiser for IEEE-754 floating-point values description: Convert Float and Decimal values to/from raw octets. license: GPL license-file: License.txt@@ -13,8 +13,8 @@ bug-reports: mailto:jmillikin@gmail.com  source-repository head-  type: bzr-  location: bzr+ssh://bazaar.launchpad.net/~jmillikin/%2Bjunk/data-binary-ieee754/+  type: darcs+  location: http://patch-tag.com/r/data-binary-ieee754/pullrepo  library   build-depends: base >=3 && < 5, binary, bytestring