The original blog post is here.
The Problem
If you want to sum up several fixbv
fixed-point variables, what will you do?
If your answer is as follows:
a + b + c + d
It seems to be correct but there would be a trap on the bit width.
Bit Width Changing While Adding
Here we use (total_width, integer_width, fractional_width)
to indicate the bit width of the fixbv
variable, its integer part, and its fractional part.
As written in MEP-111, if two fixed-point variables are added, the fractional part of the result should be the longest of the operands; and the integer part of the result should be the longest plus one, in order to avoid overflow.
This is an example of adding a variable in format (8, 3, 4)
and another variable in format (8, 0, 7)
:
siii.ffff000
+ ssss.fffffff
--------------
siiii.fffffff
We can see that the first operand has been added zeros in the tail of the fractional part, and the second operand has been added sign bits in the beginning, in order to perform point alignment.
A Critical Example
Considering the following situation:
>>> a.format
(16, 4, 11)
>>> b.format
(16, 4, 11)
>>> c.format
(8, 3, 4)
>>> d.format
(8, 7, 0)
If we add like a + b + c + d
, the format of the final result will be:
(16, 4, [11])+(16, 4, [11])+(8, 3, [4])+(8, 7, [0])
=(17, 5, [11])+(8, 3, [4])+(8, 7, [0])
=(18, 6, [11])+(8, 7, [0])
=(20, 8, [11])
However, if we add in d + a + b + c
, the result should be:
(8, 7, [0])+(16, 4, [11])+(16, 4, [11])+(8, 3, [4])
=(20, 8, [11])+(16, 4, [11])+(8, 3, [4])
=(21, 9, [11])+(8, 3, [4])
=(22, 10, [11])
Assignments
If we use the assignment like:
x = a + b + c + d
this problem should be consider. But, if we have already defined the format of x and perform the operation as
x[:] = a + b + c + d
it would be safer because the format of x
has already been decided. However, rounding and overflow must be considered if necessary.
A New Sum Function for Fixed-Point Variables
So here, in order to make the result unique in different orders, a new function fxsum
is needed in this theme. The usage is similar to built-in sum
:
fxsum(iterable, start=0)
fxsum
requires an iterable parameter containing fixbv
instances, and returns the sum as a fixbv
variable. The width of the fractional part would be the longest in the iterable, while the width of the width of the integer part is the longest plus ceil(log(N, 2))
, in which N
is the width of the longest integer part.
Instead of using a + b + c + d
, we could write
fxsum((a, b, c, d))
to avoid ambiguity of bit width in the result.
As a result, in the previous example, we could obtain that
>>> fxsum((a, b, c, d)).format
(21, 9, [11])