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

1import unittest 

2 

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) 

11 

12 

13class FloatUtilTest(unittest.TestCase): 

14 

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)) 

19 

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)) 

23 

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)) 

29 

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)) 

35 

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)) 

41 

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)) 

47 

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)) 

51 

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)) 

56 

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)) 

59 

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)) 

65 

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)) 

72 

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)) 

77 

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)) 

81 

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)) 

87 

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)) 

93 

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)) 

99 

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)) 

105 

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)) 

109 

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)) 

114 

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)) 

117 

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)) 

122 

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)) 

128 

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)) 

136 

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)) 

141 

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)) 

147 

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)) 

152 

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)) 

158 

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) 

164 

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) 

170 

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) 

176 

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) 

182 

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 ) 

189 

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 ) 

196 

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 ) 

203 

204 FLOAT16_SIGN_BIT_POSITION = 15 

205 FLOAT16_EXPONENT_BIT_POSITION = 10 

206 

207 FLOAT32_SIGN_BIT_POSITION = 31 

208 FLOAT32_EXPONENT_BIT_POSITION = 23 

209 

210 FLOAT64_SIGN_BIT_POSITION = 63 

211 FLOAT64_EXPONENT_BIT_POSITION = 52 

212 

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 ] 

223 

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 ]