Coverage for /home/runner/work/zserio/zserio/compiler/extensions/python/runtime/tests/test_float.py: 100%
127 statements
« prev ^ index » next coverage.py v6.5.0, created at 2024-12-05 10:43 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2024-12-05 10:43 +0000
1import unittest
3from zserio.float import (
4 uint16_to_float,
5 float_to_uint16,
6 uint32_to_float,
7 float_to_uint32,
8 uint64_to_float,
9 float_to_uint64,
10)
13class FloatUtilTest(unittest.TestCase):
15 def test_uint16_to_float(self):
16 # plus zero
17 float16_value_plus_zero = self._create_float16_value(0, 0, 0) # +0.0
18 self.assertEqual(0.0, uint16_to_float(float16_value_plus_zero))
20 # minus zero
21 float16_value_minus_zero = self._create_float16_value(1, 0, 0) # -0.0
22 self.assertEqual(-0.0, uint16_to_float(float16_value_minus_zero))
24 # plus infinity
25 float16_value_plus_infinity = self._create_float16_value(0, 0x1F, 0) # +INF
26 float64_value_plus_infinity = self._create_float64_value(0, 0x7FF, 0) # +INF
27 converted_float = uint16_to_float(float16_value_plus_infinity)
28 self.assertEqual(float64_value_plus_infinity, float_to_uint64(converted_float))
30 # minus infinity
31 float16_value_minus_infinity = self._create_float16_value(1, 0x1F, 0) # -INF
32 float64_value_minus_infinity = self._create_float64_value(1, 0x7FF, 0) # -INF
33 converted_float = uint16_to_float(float16_value_minus_infinity)
34 self.assertEqual(float64_value_minus_infinity, float_to_uint64(converted_float))
36 # quiet NaN
37 float16_value_quiet_nan = self._create_float16_value(0, 0x1F, 0x3FF) # +NaN
38 float64_value_quiet_nan = self._create_float64_value(0, 0x7FF, 0xFFC0000000000) # +NaN
39 converted_float = uint16_to_float(float16_value_quiet_nan)
40 self.assertEqual(float64_value_quiet_nan, float_to_uint64(converted_float))
42 # signaling NaN
43 float16_value_signaling_nan = self._create_float16_value(1, 0x1F, 0x3FF) # -NaN
44 float64_value_signaling_nan = self._create_float64_value(1, 0x7FF, 0xFFC0000000000) # -NaN
45 converted_float = uint16_to_float(float16_value_signaling_nan)
46 self.assertEqual(float64_value_signaling_nan, float_to_uint64(converted_float))
48 # normal numbers
49 float16_value_one = self._create_float16_value(0, 15, 0) # 1.0
50 self.assertEqual(1.0, uint16_to_float(float16_value_one))
52 float16_value_one_plus = self._create_float16_value(0, 15, 0x01) # 1.0 + 2^-10
53 float64_value_one_plus = self._create_float64_value(0, 0x3FF, 0x40000000000) # 1.0 + 2^-10
54 converted_float = uint16_to_float(float16_value_one_plus)
55 self.assertEqual(float64_value_one_plus, float_to_uint64(converted_float))
57 float16_value_max = self._create_float16_value(0, 30, 0x3FF) # 2^15 (1 + 2^-1 + ... + 2^-10)
58 self.assertEqual(65504.0, uint16_to_float(float16_value_max))
60 # subnormal numbers
61 float16_value_min_subnormal = self._create_float16_value(0, 0, 1) # 2^-14 (2^-10)
62 float64_value_min_subnormal = self._create_float64_value(0, 999, 0) # 2^-24
63 converted_float = uint16_to_float(float16_value_min_subnormal)
64 self.assertEqual(float64_value_min_subnormal, float_to_uint64(converted_float))
66 float16_value_max_subnormal = self._create_float16_value(0, 0, 0x3FF) # 2^-14 (2^-1 + ... + 2^-10)
67 float64_value_max_subnormal = self._create_float64_value(
68 0, 1008, 0xFF80000000000
69 ) # 2^-15 (1 + 2^-1 + ... + 2^-9)
70 converted_float = uint16_to_float(float16_value_max_subnormal)
71 self.assertEqual(float64_value_max_subnormal, float_to_uint64(converted_float))
73 def test_float_to_uint16(self):
74 # plus zero
75 float16_value_plus_zero = self._create_float16_value(0, 0, 0) # +0.0
76 self.assertEqual(float16_value_plus_zero, float_to_uint16(0.0))
78 # minus zero
79 float16_value_minus_zero = self._create_float16_value(1, 0, 0) # -0.0
80 self.assertEqual(float16_value_minus_zero, float_to_uint16(-0.0))
82 # plus infinity
83 float64_value_plus_infinity = self._create_float64_value(0, 0x7FF, 0) # +INF
84 float16_value_plus_infinity = self._create_float16_value(0, 0x1F, 0) # +INF
85 converted_float = uint64_to_float(float64_value_plus_infinity)
86 self.assertEqual(float16_value_plus_infinity, float_to_uint16(converted_float))
88 # minus infinity
89 float64_value_minus_infinity = self._create_float64_value(1, 0x7FF, 0) # -INF
90 float16_value_minus_infinity = self._create_float16_value(1, 0x1F, 0) # -INF
91 converted_float = uint64_to_float(float64_value_minus_infinity)
92 self.assertEqual(float16_value_minus_infinity, float_to_uint16(converted_float))
94 # quiet NaN
95 float64_value_quiet_nan = self._create_float64_value(0, 0x7FF, 0xFFC0000000000) # +NaN
96 float16_value_quiet_nan = self._create_float16_value(0, 0x1F, 0x3FF) # +NaN
97 converted_float = uint64_to_float(float64_value_quiet_nan)
98 self.assertEqual(float16_value_quiet_nan, float_to_uint16(converted_float))
100 # signaling NaN
101 float64_value_signaling_nan = self._create_float64_value(1, 0x7FF, 0xFFC0000000000) # -NaN
102 float16_value_signaling_nan = self._create_float16_value(1, 0x1F, 0x3FF) # -NaN
103 converted_float = uint64_to_float(float64_value_signaling_nan)
104 self.assertEqual(float16_value_signaling_nan, float_to_uint16(converted_float))
106 # normal numbers
107 float16_value_one = self._create_float16_value(0, 15, 0) # 1.0
108 self.assertEqual(float16_value_one, float_to_uint16(1.0))
110 float64_value_one_plus = self._create_float64_value(0, 0x3FF, 0x40000000000) # 1.0 + 2^-10
111 float16_value_one_plus = self._create_float16_value(0, 15, 0x01) # 1.0 + 2^-10
112 converted_float = uint64_to_float(float64_value_one_plus)
113 self.assertEqual(float16_value_one_plus, float_to_uint16(converted_float))
115 float16_value_max = self._create_float16_value(0, 30, 0x3FF) # 2^15 (1 + 2^-1 + ... + 2^-10)
116 self.assertEqual(float16_value_max, float_to_uint16(65504.0))
118 # normal numbers converted to zero
119 float64_value_underflow = self._create_float64_value(0, 998, 0) # 2^-25
120 converted_float = uint64_to_float(float64_value_underflow)
121 self.assertEqual(float16_value_plus_zero, float_to_uint16(converted_float))
123 # normal numbers converted to subnormal numbers
124 float64_value_min_underflow = self._create_float64_value(0, 999, 1) # 2^-24 (1 + 2^-52)
125 float16_value_min_subnormal = self._create_float16_value(0, 0, 1) # 2^-24
126 converted_float = uint64_to_float(float64_value_min_underflow)
127 self.assertEqual(float16_value_min_subnormal, float_to_uint16(converted_float))
129 # normal numbers converted to subnormal numbers with rounding
130 float64_value_min_underflow_rounding = self._create_float64_value(
131 0, 1000, 0x4000000000000
132 ) # 2^-23 (1 + 2^-2)
133 float16_value_min_subnormal_rounding = self._create_float16_value(0, 0, 0x3) # 2^-14 (2^-9 + 2^-10)
134 converted_float = uint64_to_float(float64_value_min_underflow_rounding)
135 self.assertEqual(float16_value_min_subnormal_rounding, float_to_uint16(converted_float))
137 # normal numbers converted to infinity
138 float64_value_overflow = self._create_float64_value(0, 1040, 0) # 2^17
139 converted_float = uint64_to_float(float64_value_overflow)
140 self.assertEqual(float16_value_plus_infinity, float_to_uint16(converted_float))
142 # normal numbers converted with rounding
143 float64_value_rounding = self._create_float64_value(0, 1023, 0x8040000000000) # 1 + 2^-1 + 2^-11
144 float16_value_rounding = self._create_float16_value(0, 15, 0x201) # 1 + 2^-1 + 2^-10
145 converted_float = uint64_to_float(float64_value_rounding)
146 self.assertEqual(float16_value_rounding, float_to_uint16(converted_float))
148 # subnormal numbers
149 float64_value_min32_subnormal = self._create_float64_value(0, 874, 0) # 2^-126 (2^-23)
150 converted_float = uint64_to_float(float64_value_min32_subnormal)
151 self.assertEqual(float16_value_plus_zero, float_to_uint16(converted_float))
153 float64_value_max32_subnormal = self._create_float64_value(
154 0, 896, 0xFFFFFC0000000
155 ) # 2^-126 (2^-1 + ... + 2^-23)
156 converted_float = uint64_to_float(float64_value_max32_subnormal)
157 self.assertEqual(float16_value_plus_zero, float_to_uint16(converted_float))
159 def test_uint32_to_float(self):
160 for data_row in self.TEST_FLOAT32_DATA:
161 float32_value = self._create_float32_value(data_row[0], data_row[1], data_row[2])
162 converted_float = uint32_to_float(float32_value)
163 self.assertEqual(data_row[3], converted_float)
165 def test_float_to_uint32(self):
166 for data_row in self.TEST_FLOAT32_DATA:
167 converted_float32_value = float_to_uint32(data_row[3])
168 float32_value = self._create_float32_value(data_row[0], data_row[1], data_row[2])
169 self.assertEqual(float32_value, converted_float32_value)
171 def test_uint64_to_float(self):
172 for data_row in self.TEST_FLOAT64_DATA:
173 float64_value = self._create_float64_value(data_row[0], data_row[1], data_row[2])
174 converted_float = uint64_to_float(float64_value)
175 self.assertEqual(data_row[3], converted_float)
177 def test_float_to_uint64(self):
178 for data_row in self.TEST_FLOAT64_DATA:
179 converted_float64_value = float_to_uint64(data_row[3])
180 float64_value = self._create_float64_value(data_row[0], data_row[1], data_row[2])
181 self.assertEqual(float64_value, converted_float64_value)
183 def _create_float16_value(self, sign, exponent, significand):
184 return (
185 (sign << self.FLOAT16_SIGN_BIT_POSITION)
186 | (exponent << self.FLOAT16_EXPONENT_BIT_POSITION)
187 | significand
188 )
190 def _create_float32_value(self, sign, exponent, significand):
191 return (
192 (sign << self.FLOAT32_SIGN_BIT_POSITION)
193 | (exponent << self.FLOAT32_EXPONENT_BIT_POSITION)
194 | significand
195 )
197 def _create_float64_value(self, sign, exponent, significand):
198 return (
199 (sign << self.FLOAT64_SIGN_BIT_POSITION)
200 | (exponent << self.FLOAT64_EXPONENT_BIT_POSITION)
201 | significand
202 )
204 FLOAT16_SIGN_BIT_POSITION = 15
205 FLOAT16_EXPONENT_BIT_POSITION = 10
207 FLOAT32_SIGN_BIT_POSITION = 31
208 FLOAT32_EXPONENT_BIT_POSITION = 23
210 FLOAT64_SIGN_BIT_POSITION = 63
211 FLOAT64_EXPONENT_BIT_POSITION = 52
213 TEST_FLOAT32_DATA = [
214 [0, 0, 0, 0.0],
215 [1, 0, 0, -0.0],
216 [0, 127, 0, +1.0],
217 [1, 127, 0, -1.0],
218 [0, 128, 0x600000, 3.5], # 2^1 (1 + 2^-1 + 2^-2)
219 [0, 126, 0x600000, 0.875], # 2^-1 (1 + 2^-1 + 2^-2)
220 [0, 130, 0x1E0000, 9.875], # 2^3 (1 + 2^-3 + 2^-4 + 2^-5 + 2^-6)
221 [0, 126, 0x1E0000, 0.6171875], # 2^-3 (1 + 2^-3 + 2^-4 + 2^-5 + 2^-6)
222 ]
224 TEST_FLOAT64_DATA = [
225 [0, 0, 0, 0.0],
226 [1, 0, 0, -0.0],
227 [0, 1023, 0, +1.0],
228 [1, 1023, 0, -1.0],
229 [0, 1024, 0xC000000000000, 3.5], # 2^1 (1 + 2^-1 + 2^-2)
230 [0, 1022, 0xC000000000000, 0.875], # 2^-1 (1 + 2^-1 + 2^-2)
231 [0, 1026, 0x3C00000000000, 9.875], # 2^3 (1 + 2^-3 + 2^-4 + 2^-5 + 2^-6)
232 [0, 1022, 0x3C00000000000, 0.6171875], # 2^-3 (1 + 2^-3 + 2^-4 + 2^-5 + 2^-6)
233 ]