Coverage for /home/jenkins/workspace/NDS/Zserio/NDS_ZSERIO-linux-build/compiler/extensions/python/runtime/src/zserio/float.py: 100%

82 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-12-13 15:12 +0000

1""" 

2The module provides help methods for manipulation with float numbers. 

3 

4The following float formats defined by IEEE 754 standard are supported: 

5 

6* half precision float point format (https://en.wikipedia.org/wiki/Half-precision_floating-point_format) 

7* single precision float point format (https://en.wikipedia.org/wiki/Single-precision_floating-point_format) 

8* double precision float point format (https://en.wikipedia.org/wiki/Double-precision_floating-point_format) 

9""" 

10 

11import struct 

12 

13def uint16_to_float(float16_value: int) -> float: 

14 """ 

15 Converts 16-bit float stored as an integer value to python native float. 

16 

17 :param float16_value: Half precision float value stored as an integer value to convert. 

18 :returns: Converted python native float. 

19 """ 

20 

21 # decompose half precision float (float16) 

22 sign16_shifted = float16_value & FLOAT16_SIGN_MASK 

23 exponent16 = (float16_value & FLOAT16_EXPONENT_MASK) >> FLOAT16_EXPONENT_BIT_POSITION 

24 significand16 = float16_value & FLOAT16_SIGNIFICAND_MASK 

25 

26 # calculate significand for single precision float (float32) 

27 significand32 = significand16 << (FLOAT32_SIGNIFICAND_NUM_BITS - FLOAT16_SIGNIFICAND_NUM_BITS) 

28 

29 # calculate exponent for single precision float (float32) 

30 if exponent16 == 0: 

31 if significand32 != 0: 

32 # subnormal (denormal) number will be normalized 

33 exponent32 = (1 + FLOAT32_EXPONENT_BIAS - 

34 FLOAT16_EXPONENT_BIAS) # exp is initialized by -14 

35 # shift significand until leading bit overflows into exponent bit 

36 while (significand32 & (FLOAT32_SIGNIFICAND_MASK + 1)) == 0: 

37 exponent32 = exponent32 - 1 

38 significand32 <<= 1 

39 

40 # mask out overflowed leading bit from significand (normalized has implicit leading bit 1) 

41 significand32 &= FLOAT32_SIGNIFICAND_MASK 

42 else: 

43 # zero 

44 exponent32 = 0 

45 elif exponent16 == FLOAT16_EXPONENT_INFINITY_NAN: 

46 # infinity or NaN 

47 exponent32 = FLOAT32_EXPONENT_INFINITY_NAN 

48 else: 

49 # normal number 

50 exponent32 = exponent16 - FLOAT16_EXPONENT_BIAS + FLOAT32_EXPONENT_BIAS 

51 

52 # compose single precision float (float32) 

53 sign32_shifted = sign16_shifted << (FLOAT32_SIGN_BIT_POSITION - FLOAT16_SIGN_BIT_POSITION) 

54 exponent32_shifted = exponent32 << FLOAT32_EXPONENT_BIT_POSITION 

55 float32_value = sign32_shifted | exponent32_shifted | significand32 

56 

57 # convert it to float 

58 return uint32_to_float(float32_value) 

59 

60def float_to_uint16(float64: float) -> int: 

61 """ 

62 Converts python native float to 16-bit float stored as integer value. 

63 

64 :param float64: Python native float to convert. 

65 :returns: Converted half precision float value stored as an integer value. 

66 """ 

67 

68 float32_value = float_to_uint32(float64) 

69 

70 # decompose single precision float (float32) 

71 sign32_shifted = float32_value & FLOAT32_SIGN_MASK 

72 exponent32 = (float32_value & FLOAT32_EXPONENT_MASK) >> FLOAT32_EXPONENT_BIT_POSITION 

73 significand32 = float32_value & FLOAT32_SIGNIFICAND_MASK 

74 

75 # calculate significand for half precision float (float16) 

76 significand16 = significand32 >> (FLOAT32_SIGNIFICAND_NUM_BITS - FLOAT16_SIGNIFICAND_NUM_BITS) 

77 

78 # calculate exponent for half precision float (float16) 

79 needs_rounding = False 

80 if exponent32 == 0: 

81 if significand32 != 0: 

82 # subnormal (denormal) number will be zero 

83 significand16 = 0 

84 exponent16 = 0 

85 elif exponent32 == FLOAT32_EXPONENT_INFINITY_NAN: 

86 # infinity or NaN 

87 exponent16 = FLOAT16_EXPONENT_INFINITY_NAN 

88 else: 

89 # normal number 

90 signed_exponent16 = exponent32 - FLOAT32_EXPONENT_BIAS + FLOAT16_EXPONENT_BIAS 

91 if signed_exponent16 > FLOAT16_EXPONENT_INFINITY_NAN: 

92 # exponent overflow, set infinity or NaN 

93 exponent16 = FLOAT16_EXPONENT_INFINITY_NAN 

94 elif signed_exponent16 <= 0: 

95 # exponent underflow 

96 if signed_exponent16 <= -FLOAT16_SIGNIFICAND_NUM_BITS: 

97 # too big underflow, set to zero 

98 exponent16 = 0 

99 significand16 = 0 

100 else: 

101 # we can still use subnormal numbers 

102 exponent16 = 0 

103 full_significand32 = significand32 | (FLOAT32_SIGNIFICAND_MASK + 1) 

104 significand_shift = 1 - signed_exponent16 

105 significand16 = full_significand32 >> (FLOAT32_SIGNIFICAND_NUM_BITS - 

106 FLOAT16_SIGNIFICAND_NUM_BITS + significand_shift) 

107 

108 needs_rounding = ((full_significand32 >> 

109 (FLOAT32_SIGNIFICAND_NUM_BITS - FLOAT16_SIGNIFICAND_NUM_BITS + 

110 significand_shift - 1)) & 1) != 0 

111 else: 

112 # exponent ok 

113 exponent16 = signed_exponent16 

114 needs_rounding = ((significand32 >> (FLOAT32_SIGNIFICAND_NUM_BITS - 

115 FLOAT16_SIGNIFICAND_NUM_BITS - 1)) & 1) != 0 

116 

117 # compose half precision float (float16) 

118 sign16_shifted = sign32_shifted >> (FLOAT32_SIGN_BIT_POSITION - FLOAT16_SIGN_BIT_POSITION) 

119 exponent16_shifted = exponent16 << FLOAT16_EXPONENT_BIT_POSITION 

120 float16_value = sign16_shifted | exponent16_shifted | significand16 

121 

122 # check rounding 

123 if needs_rounding: 

124 float16_value += 1 # might overflow to infinity 

125 

126 return float16_value 

127 

128def uint32_to_float(float32_value: int) -> float: 

129 """ 

130 Converts 32-bit float stored as an integer value to python native float. 

131 

132 :param float32_value: Single precision float value stored as an integer value to convert. 

133 :returns: Converted python native float. 

134 """ 

135 

136 float32_value_in_bytes = float32_value.to_bytes(4, byteorder="big") 

137 

138 return struct.unpack('>f', float32_value_in_bytes)[0] 

139 

140def float_to_uint32(float64: float) -> int: 

141 """ 

142 Converts python native float to 32-bit float stored as integer value. 

143 

144 :param float64: Python native float to convert. 

145 :returns: Converted single precision float value stored as an integer value. 

146 """ 

147 

148 float32_value_in_bytes = struct.pack('>f', float64) 

149 

150 return int.from_bytes(float32_value_in_bytes, byteorder="big") 

151 

152def uint64_to_float(float64_value: int) -> float: 

153 """ 

154 Converts 64-bit float stored as an integer value to python native float. 

155 

156 :param float64_value: Double precision float value stored as an integer value to convert. 

157 :returns: Converted python native float. 

158 """ 

159 

160 float64_value_in_bytes = float64_value.to_bytes(8, byteorder="big") 

161 

162 return struct.unpack('>d', float64_value_in_bytes)[0] 

163 

164def float_to_uint64(float64: float) -> int: 

165 """ 

166 Converts python native float to 64-bit float stored as integer value. 

167 

168 :param float64: Python native float to convert. 

169 :returns: Converted double precision float value stored as an integer value. 

170 """ 

171 

172 float64_value_in_bytes = struct.pack('>d', float64) 

173 

174 return int.from_bytes(float64_value_in_bytes, byteorder="big") 

175 

176FLOAT16_SIGN_MASK = 0x8000 

177FLOAT16_EXPONENT_MASK = 0x7C00 

178FLOAT16_SIGNIFICAND_MASK = 0x03FF 

179 

180FLOAT16_SIGN_BIT_POSITION = 15 

181FLOAT16_EXPONENT_BIT_POSITION = 10 

182 

183FLOAT16_SIGNIFICAND_NUM_BITS = FLOAT16_EXPONENT_BIT_POSITION 

184 

185FLOAT16_EXPONENT_INFINITY_NAN = 0x001F 

186FLOAT16_EXPONENT_BIAS = 15 

187 

188FLOAT32_SIGN_MASK = 0x80000000 

189FLOAT32_EXPONENT_MASK = 0x7F800000 

190FLOAT32_SIGNIFICAND_MASK = 0x007FFFFF 

191 

192FLOAT32_SIGN_BIT_POSITION = 31 

193FLOAT32_EXPONENT_BIT_POSITION = 23 

194 

195FLOAT32_SIGNIFICAND_NUM_BITS = FLOAT32_EXPONENT_BIT_POSITION 

196 

197FLOAT32_EXPONENT_INFINITY_NAN = 0x00FF 

198FLOAT32_EXPONENT_BIAS = 127