Skip to content

Commit ae6199d

Browse files
Fix a few issues raised in PR#3 review.
- remove extra code / spurious comments - radically simplify FloatImmediate checkValue - remove offset and n_levels from FloatAddTemplate
1 parent df702a6 commit ae6199d

File tree

3 files changed

+26
-103
lines changed

3 files changed

+26
-103
lines changed

Deeploy/AbstractDataTypes.py

Lines changed: 22 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -236,9 +236,9 @@ def checkValue(cls, value: Union[int, Iterable[int]], ctxt: Optional[_NetworkCon
236236

237237

238238
class FloatImmediate(Immediate[Union[float, Iterable[float]], _ImmediateType]):
239-
typeFraction: int #: int: Represents the number of bits reserved for the fraction part
240-
typeExponent: int #: int: Represents the number of bits reserved for the exponent part
241-
signed: bool #: bool: Represents whether the underlying float is signed or unsigned (should be removed)
239+
# FIXME: check typeFraction vs typeMantissa
240+
typeFraction: int #: int: Represents the number of bits reserved for the fraction part
241+
typeExponent: int #: int: Represents the number of bits reserved for the exponent part
242242

243243
@_classproperty
244244
def typeExponentMax(cls) -> int:
@@ -250,7 +250,6 @@ def typeExponentOffset(cls) -> int:
250250
# The offset added to the exponent
251251
return 2**(cls.typeExponent - 1) - 1
252252

253-
# ADEQUINO: This is a ugly workaround for FP, works for bfloat16 and fp32 because bfloat16 is a truncated fp32
254253
@classmethod
255254
def partialOrderUpcast(cls, otherCls: Type[Immediate]) -> bool:
256255
if issubclass(otherCls, FloatImmediate):
@@ -277,92 +276,29 @@ def checkValue(cls, value: Union[float, Iterable[float]], ctxt: Optional[_Networ
277276
else:
278277
raise Exception("Immediate type not recognized.")
279278

279+
# The exponent bias for FP64 is 2**(11-1)-1 as the exponent has 11 bits.
280+
DOUBLE_MIN_EXP = -1023
281+
280282
for val in _val_list:
281-
# Zero (and subnormals, not implemented) are special cases
282-
if (val == 0):
283+
284+
# Extract mantissa, exponent, and sign.
285+
# Also bring mantissa and exponent to IEEE754 compliant form for non-denormals.
286+
mantissa, exponent = math.frexp(val)
287+
sign = True if mantissa < 0 else False
288+
mantissa = -mantissa*2 if sign else mantissa*2
289+
exponent -= 1
290+
291+
# Check if the number is finite, nonzero and not denormal, otherwise skip the check.
292+
if not (math.isfinite(val) and val != 0 and exponent > DOUBLE_MIN_EXP):
283293
continue
284-
# Make the value positive
285-
if (val < 0):
286-
val = val * -1
287-
288-
# Separate Integer and Fraction of immediate
289-
fraction, integer = math.modf(val)
290-
291-
# Binarylist for the mantissa
292-
binarylist = []
293-
f = fraction
294-
295-
# Fraction binarization, fails if nbits required > n bits mantissa.
296-
# If integer part of immediate is 0, we start counting mantissa bits after we find the first 1 bit.
297-
if (int(integer) > 0):
298-
for i in range(cls.typeFraction):
299-
f = f * 2
300-
f, fint = math.modf(f)
301-
binarylist.append(str(int(fint)))
302-
if f == 0:
303-
break
304-
elif i == (cls.typeFraction - 1):
305-
return False
306-
else:
307-
flag = 0
308-
count = cls.typeFraction + 1
309-
while (count):
310-
f = f * 2
311-
f, fint = math.modf(f)
312-
binarylist.append(str(int(fint)))
313-
if int(fint) == 1 and flag == 0:
314-
flag = 1
315-
if f == 0:
316-
break
317-
if flag == 1:
318-
count = count - 1
319-
if (count == 0):
320-
return False
321-
322-
# Float exponent part
323-
# It's equal to the length of the integer part minus 1, if the integer part is not zero.
324-
# Otherwise, it's minus the number of 0 bits before the first 1 bit in the fraction representation + 1
325-
exponent = 0
326-
if (int(bin(int(integer))[2:]) == 0):
327-
for b in binarylist:
328-
exponent = exponent - 1
329-
if b == '1':
330-
break
331-
else:
332-
exponent = len(str(bin(int(integer))[2:])) - 1
333-
334-
# Check if exponent is representable in n_exponent bits
335-
true_exponent = int(bin(cls.typeExponentOffset + exponent)[2:])
294+
295+
# Check if exponent is representable.
336296
if (cls.typeExponentOffset + exponent) > cls.typeExponentMax or (cls.typeExponentOffset + exponent) < 0:
337297
return False
338-
339-
# Append bits to head of mantissa, if integer part is not in scientific notion
340-
binarylist2 = []
341-
if len(str(bin(int(integer))[2:])) > 1:
342-
for digit in str(bin(int(integer))[3:]):
343-
binarylist2.append((digit))
344-
345-
# If integer part is zero, trim the mantissa bits that have been used to calculate the exponent part
346-
if (int(integer) > 0):
347-
finalbinaryfraction = binarylist2 + binarylist
348-
else:
349-
finalbinaryfraction = binarylist
350-
while (finalbinaryfraction[0] == '0'):
351-
finalbinaryfraction.pop(0)
352-
finalbinaryfraction.pop(0)
353-
354-
# Fix mantissa size
355-
if ((cls.typeFraction - len(finalbinaryfraction)) > 0):
356-
finalbinaryfraction += ['0'] * (cls.typeFraction - len(finalbinaryfraction))
357-
if (len(finalbinaryfraction) > cls.typeFraction):
358-
finalbinaryfraction = finalbinaryfraction[:cls.typeFraction]
359-
360-
# Check if the value in binary float represent the immediate value
361-
exponent_part = 2**exponent
362-
mantissa_part = 1
363-
for (i, m) in enumerate(finalbinaryfraction):
364-
mantissa_part = mantissa_part + 2**(-(i + 1)) * int(m)
365-
if (exponent_part * mantissa_part != val):
298+
299+
# Check if mantissa is representable. Implicit assumption is that cls.typeFraction < 52 (like in FP64)
300+
truncated_mantissa = 1 + math.floor((2 ** cls.typeFraction) * (mantissa-1)) / (2 ** cls.typeFraction)
301+
if math.fabs(truncated_mantissa - mantissa) > 0.0:
366302
return False
367303

368304
return True

Deeploy/CommonExtensions/DataTypes.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,20 +76,19 @@ class uint64_t(IntegerImmediate):
7676
signed = False
7777

7878

79-
class bfloat16(FloatImmediate):
79+
# BFloat16 in PULP systems
80+
class float16alt(FloatImmediate):
8081
typeName = "float16alt"
8182
typeWidth = 16
8283
typeFraction = 7
8384
typeExponent = 8
84-
signed = True
8585

8686

8787
class float32(FloatImmediate):
8888
typeName = "float"
8989
typeWidth = 32
9090
typeFraction = 23
9191
typeExponent = 8
92-
signed = True
9392

9493

9594
SignedIntegerDataTypes: Tuple[Type[IntegerImmediate], ...] = (int8_t, int16_t, int32_t, int64_t)
@@ -99,4 +98,4 @@ class float32(FloatImmediate):
9998
*UnsignedIntegerDataTypes,
10099
),
101100
key = lambda _type: _type.typeWidth))
102-
FloatDataTypes: Tuple[Type[FloatImmediate], ...] = (bfloat16, float32)
101+
FloatDataTypes: Tuple[Type[FloatImmediate], ...] = (float16alt, float32)

Deeploy/Targets/Generic/Templates/FloatAddTemplate.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,14 @@ def alignToContext(self, ctxt: NetworkContext,
3737
data_in_2 = ctxt.lookup(operatorRepresentation['data_in_2'])
3838
data_out = ctxt.lookup(operatorRepresentation['data_out'])
3939

40-
input_1_offset = 0
41-
if hasattr(data_in_1, "_signed") and hasattr(data_in_1, "nLevels"):
42-
input_1_offset = (data_in_1._signed == 0) * int(data_in_1.nLevels / 2)
43-
input_2_offset = 0
44-
if hasattr(data_in_2, "_signed") and hasattr(data_in_2, "nLevels"):
45-
input_2_offset = (data_in_2._signed == 0) * int(data_in_2.nLevels / 2)
46-
output_offset = 0
47-
if hasattr(data_out, "_signed") and hasattr(data_out, "nLevels"):
48-
output_offset = -(data_out._signed == 0) * int(data_out.nLevels // 2)
49-
50-
operatorRepresentation['offset'] = input_1_offset + input_2_offset + output_offset
51-
5240
return ctxt, operatorRepresentation, []
5341

5442

5543
referenceTemplate = _FloatAddTemplate("""
5644
// Add (Name: ${nodeName}, Op: ${nodeOp})
5745
BEGIN_SINGLE_CORE
5846
for (uint32_t i=0;i<${size};i++){
59-
${data_out}[i] = ${data_in_1}[i] + ${data_in_2}[i] + ${offset};
47+
${data_out}[i] = ${data_in_1}[i] + ${data_in_2}[i];
6048
}
6149
END_SINGLE_CORE
6250
""")

0 commit comments

Comments
 (0)