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
« 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.
4The following float formats defined by IEEE 754 standard are supported:
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"""
11import struct
13def uint16_to_float(float16_value: int) -> float:
14 """
15 Converts 16-bit float stored as an integer value to python native float.
17 :param float16_value: Half precision float value stored as an integer value to convert.
18 :returns: Converted python native float.
19 """
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
26 # calculate significand for single precision float (float32)
27 significand32 = significand16 << (FLOAT32_SIGNIFICAND_NUM_BITS - FLOAT16_SIGNIFICAND_NUM_BITS)
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
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
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
57 # convert it to float
58 return uint32_to_float(float32_value)
60def float_to_uint16(float64: float) -> int:
61 """
62 Converts python native float to 16-bit float stored as integer value.
64 :param float64: Python native float to convert.
65 :returns: Converted half precision float value stored as an integer value.
66 """
68 float32_value = float_to_uint32(float64)
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
75 # calculate significand for half precision float (float16)
76 significand16 = significand32 >> (FLOAT32_SIGNIFICAND_NUM_BITS - FLOAT16_SIGNIFICAND_NUM_BITS)
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)
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
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
122 # check rounding
123 if needs_rounding:
124 float16_value += 1 # might overflow to infinity
126 return float16_value
128def uint32_to_float(float32_value: int) -> float:
129 """
130 Converts 32-bit float stored as an integer value to python native float.
132 :param float32_value: Single precision float value stored as an integer value to convert.
133 :returns: Converted python native float.
134 """
136 float32_value_in_bytes = float32_value.to_bytes(4, byteorder="big")
138 return struct.unpack('>f', float32_value_in_bytes)[0]
140def float_to_uint32(float64: float) -> int:
141 """
142 Converts python native float to 32-bit float stored as integer value.
144 :param float64: Python native float to convert.
145 :returns: Converted single precision float value stored as an integer value.
146 """
148 float32_value_in_bytes = struct.pack('>f', float64)
150 return int.from_bytes(float32_value_in_bytes, byteorder="big")
152def uint64_to_float(float64_value: int) -> float:
153 """
154 Converts 64-bit float stored as an integer value to python native float.
156 :param float64_value: Double precision float value stored as an integer value to convert.
157 :returns: Converted python native float.
158 """
160 float64_value_in_bytes = float64_value.to_bytes(8, byteorder="big")
162 return struct.unpack('>d', float64_value_in_bytes)[0]
164def float_to_uint64(float64: float) -> int:
165 """
166 Converts python native float to 64-bit float stored as integer value.
168 :param float64: Python native float to convert.
169 :returns: Converted double precision float value stored as an integer value.
170 """
172 float64_value_in_bytes = struct.pack('>d', float64)
174 return int.from_bytes(float64_value_in_bytes, byteorder="big")
176FLOAT16_SIGN_MASK = 0x8000
177FLOAT16_EXPONENT_MASK = 0x7C00
178FLOAT16_SIGNIFICAND_MASK = 0x03FF
180FLOAT16_SIGN_BIT_POSITION = 15
181FLOAT16_EXPONENT_BIT_POSITION = 10
183FLOAT16_SIGNIFICAND_NUM_BITS = FLOAT16_EXPONENT_BIT_POSITION
185FLOAT16_EXPONENT_INFINITY_NAN = 0x001F
186FLOAT16_EXPONENT_BIAS = 15
188FLOAT32_SIGN_MASK = 0x80000000
189FLOAT32_EXPONENT_MASK = 0x7F800000
190FLOAT32_SIGNIFICAND_MASK = 0x007FFFFF
192FLOAT32_SIGN_BIT_POSITION = 31
193FLOAT32_EXPONENT_BIT_POSITION = 23
195FLOAT32_SIGNIFICAND_NUM_BITS = FLOAT32_EXPONENT_BIT_POSITION
197FLOAT32_EXPONENT_INFINITY_NAN = 0x00FF
198FLOAT32_EXPONENT_BIAS = 127