File: Tests.hs

package info (click to toggle)
haskell-data-binary-ieee754 0.4.4-13
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 72 kB
  • sloc: haskell: 243; makefile: 2
file content (244 lines) | stat: -rw-r--r-- 7,161 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
-----------------------------------------------------------------------------
-- |
-- Module: Tests
-- Copyright: 2010 John Millikin
-- License: MIT
--
-- Maintainer: jmillikin@gmail.com
-- Portability: portable
--
-----------------------------------------------------------------------------
module Main (tests, main) where

import qualified Data.ByteString.Lazy as B
import           Data.Word (Word8)
import           Data.Binary.Get (Get, runGetState)
import           Data.Binary.Put (Put, runPut)

import          Test.Chell
import          Test.Chell.QuickCheck
import          Test.QuickCheck hiding (label, property)

import          Data.Binary.IEEE754

tests :: [Suite]
tests =
	[ test_Parsing
	, test_Serialising
	, test_Passthrough
	, test_Passthrough_NaN
	]

main :: IO ()
main = Test.Chell.defaultMain tests

test_Parsing :: Suite
test_Parsing = suite "parsing"
	(props_GetFloat16 "16")
	(props_GetFloat32 "32")
	(props_GetFloat64 "64")

test_Serialising :: Suite
test_Serialising = suite "serialising"
	(props_PutFloat32 "32")
	(props_PutFloat64 "64")

test_Passthrough :: Suite
test_Passthrough = suite "passthrough"
	(testPassthrough "32-le" putFloat32le getFloat32le)
	(testPassthrough "32-be" putFloat32be getFloat32be)
	(testPassthrough "64-le" putFloat64le getFloat64le)
	(testPassthrough "64-be" putFloat64be getFloat64be)

test_Passthrough_NaN :: Suite
test_Passthrough_NaN = suite "passthrough-nan"
	(testPassthroughNaN "32-le" putFloat32le getFloat32le)
	(testPassthroughNaN "32-be" putFloat32be getFloat32be)
	(testPassthroughNaN "64-le" putFloat64le getFloat64le)
	(testPassthroughNaN "64-be" putFloat64be getFloat64be)

props_GetFloat16 :: String -> Suite
props_GetFloat16 label =
	let check = checkGet getFloat16be getFloat16le in
	suite label
	
	(check [0, 0]    ((== 0.0) .&& (not . isNegativeZero)))
	(check [0x80, 0] isNegativeZero)
	
	-- Normalised
	(check [0x3C, 0] (==  1.0))
	(check [0xBC, 0] (== -1.0))
	
	-- Denormalised
	(check [0x03, 0xFF] (==  6.097555e-5))
	(check [0x83, 0xFF] (== -6.097555e-5))
	
	-- Infinity
	(check [0x7C, 0] (==  inf32))
	(check [0xFC, 0] (== -inf32))
	
	-- NaN
	(check [0x7E, 0] (isNaN .&& (not . isNegativeNaN)))
	(check [0xFE, 0] isNegativeNaN)

props_GetFloat32 :: String -> Suite
props_GetFloat32 label =
	let check = checkGet getFloat32be getFloat32le in
	suite label
	
	(check [0, 0, 0, 0]    ((== 0.0) .&& (not . isNegativeZero)))
	(check [0x80, 0, 0, 0] isNegativeZero)
	
	-- Normalised
	(check [0x3F, 0x80, 0, 0] (==  1.0))
	(check [0xBF, 0x80, 0, 0] (== -1.0))
	
	-- Denormalised
	(check [0x00, 0x7F, 0xFF, 0xFF] (==  1.1754942106924411e-38))
	(check [0x80, 0x7F, 0xFF, 0xFF] (== -1.1754942106924411e-38))
	
	-- Infinity
	(check [0x7F, 0x80, 0, 0] (==  inf32))
	(check [0xFF, 0x80, 0, 0] (== -inf32))
	
	-- NaN and negative NaN
	(check [0x7F, 0xC0, 0, 0] (isNaN .&& (not . isNegativeNaN)))
	(check [0xFF, 0xC0, 0, 0] isNegativeNaN)

props_GetFloat64 :: String -> Suite
props_GetFloat64 label =
	let check = checkGet getFloat64be getFloat64le in
	suite label
	
	(check [0, 0, 0, 0, 0, 0, 0, 0]    ((== 0.0) .&& (not . isNegativeZero)))
	(check [0x80, 0, 0, 0, 0, 0, 0, 0] isNegativeZero)
	
	-- Normalised
	(check [0x3F, 0xF0, 0, 0, 0, 0, 0, 0] (==  1.0))
	(check [0xBF, 0xF0, 0, 0, 0, 0, 0, 0] (== -1.0))
	
	-- Denormalised
	(check [0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] (==  2.2250738585072009e-308))
	(check [0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] (== -2.2250738585072009e-308))
	
	-- Infinity
	(check [0x7F, 0xF0, 0, 0, 0, 0, 0, 0] (==  inf64))
	(check [0xFF, 0xF0, 0, 0, 0, 0, 0, 0] (== -inf64))
	
	-- NaN
	(check [0x7F, 0xF8, 0, 0, 0, 0, 0, 0] (isNaN .&& (not . isNegativeNaN)))
	(check [0xFF, 0xF8, 0, 0, 0, 0, 0, 0] isNegativeNaN)

props_PutFloat32 :: String -> Suite
props_PutFloat32 label =
	let check = checkPut putFloat32be putFloat32le in
	suite label
	
	(check [0, 0, 0, 0]   0.0)
	(check [0x80, 0, 0, 0] (-0.0))
	
	-- Normalised
	(check [0x3F, 0x80, 0, 0]   1.0)
	(check [0xBF, 0x80, 0, 0] (-1.0))
	
	-- Denormalised
	(check [0x00, 0x7F, 0xFF, 0xFF]   1.1754942106924411e-38)
	(check [0x80, 0x7F, 0xFF, 0xFF] (-1.1754942106924411e-38))
	
	-- Infinity
	(check [0x7F, 0x80, 0, 0]   inf32)
	(check [0xFF, 0x80, 0, 0] (-inf32))
	
	-- NaN
	(check [0x7F, 0xC0, 0, 0]   nan32)
	(check [0xFF, 0xC0, 0, 0] (-nan32))

props_PutFloat64 :: String -> Suite
props_PutFloat64 label =
	let check = checkPut putFloat64be putFloat64le in
	suite label
	
	(check [0, 0, 0, 0, 0, 0, 0, 0]      0.0)
	(check [0x80, 0, 0, 0, 0, 0, 0, 0] (-0.0))
	
	-- Normalised
	(check [0x3F, 0xF0, 0, 0, 0, 0, 0, 0]   1.0)
	(check [0xBF, 0xF0, 0, 0, 0, 0, 0, 0] (-1.0))
	
	-- Denormalised
	(check [0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]   2.2250738585072009e-308)
	(check [0x80, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] (-2.2250738585072009e-308))
	
	-- Infinity
	(check [0x7F, 0xF0, 0, 0, 0, 0, 0, 0]   inf64)
	(check [0xFF, 0xF0, 0, 0, 0, 0, 0, 0] (-inf64))
	
	-- NaN
	(check [0x7F, 0xF8, 0, 0, 0, 0, 0, 0]   nan64)
	(check [0xFF, 0xF8, 0, 0, 0, 0, 0, 0] (-nan64))

checkGet :: (Show a, Eq a, RealFloat a)
         => Get a -- ^ big endian
         -> Get a -- ^ little endian
         -> [Word8] -- ^ big-endian bytes
         -> (a -> Bool) -- ^ verify result
         -> Test
checkGet getBE getLE bytes f = property "get" $ forAll (return bytes) (const valid) where
	valid = B.null remainingBE && B.null remainingLE && f xBE && f xLE
	(xBE, remainingBE, _) = runGetState getBE (B.pack bytes) 0
	(xLE, remainingLE, _) = runGetState getLE (B.pack (reverse bytes)) 0

checkPut :: Show a
         => (a -> Put) -- ^ big endian
         -> (a -> Put) -- ^ little endian
         -> [Word8] -- ^ expected big-endian bytes
         -> a
         -> Test
checkPut putBE putLE bytes x = property "put" $ forAll (return x) (const valid) where
	valid = sameResult && bytes == B.unpack bytesBE
	sameResult = bytesBE == B.reverse bytesLE
	bytesBE = runPut (putBE x)
	bytesLE = runPut (putLE x)

(.&&) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
(.&&) f g x = f x && g x

isNegativeNaN :: RealFloat a => a -> Bool
isNegativeNaN x = isNaN x && frac < 0 where
	(frac, _) = decodeFloat x

-- Verify that the given put and get functions are inverses.
testPassthrough :: (Arbitrary a, Show a, Eq a)
                => String
                -> (a -> Put)
                -> Get a
                -> Test
testPassthrough name put get = property name $ \x -> let
	bytes = runPut (put x)
	(x', remaining, _) = runGetState get bytes 0
	in x == x' && B.null remaining

testPassthroughNaN :: (Arbitrary a, RealFloat a, Read a)
                    => String
                    -> (a -> Put)
                    -> Get a
                    -> Test
testPassthroughNaN name put get = property name valid where
	nan = read "NaN"
	check x = decodeFloat x == decodeFloat x' && B.null remaining where
		bytes = runPut (put x)
		(x', remaining, _) = runGetState get bytes 0
	valid = check nan && check (- nan)

-- Pseudo-literals for special values
inf32 :: Float
inf32 = read "Infinity"

inf64 :: Double
inf64 = read "Infinity"

nan32 :: Float
nan32 = - (read "NaN")

nan64 :: Double
nan64 = - (read "NaN")