Skip to content

Commit 191dc67

Browse files
vk2sebzyp
authored andcommitted
fixed.Value: remove .round() completely. rename .truncate() to .reshape()
1 parent 630cfde commit 191dc67

File tree

1 file changed

+14
-14
lines changed

1 file changed

+14
-14
lines changed

text/0041-fixed-point.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Add fixed point types to Amaranth.
1515
Fractional values in hardware are usually represented as some form of fixed point value.
1616
Without a first class fixed point type, the user has to manually manage how the value needs to be shifted to be represented as an integer and keep track of how that interacts with arithmetic operations.
1717

18-
A fixed point type would encode and keep track of the precision through arithmetic operations, as well as provide standard operations for converting values to and from fixed point representations with correct rounding.
18+
A fixed point type would encode and keep track of the precision through arithmetic operations, as well as provide standard operations for converting values to and from fixed point representations.
1919

2020
## Guide-level explanation
2121
[guide-level-explanation]: #guide-level-explanation
@@ -58,11 +58,10 @@ The following operations are defined on it:
5858
- `.eq(value)`: Assign `value`.
5959
- If `value` is a `Value`, it'll be assigned directly to the underlying `Value`.
6060
- If `value` is an `int` or `float`, it'll be cast to a `fixed.Const` first.
61-
- If `value` is a `fixed.Value`, the precision will be extended or rounded as required.
62-
- `.round(f_bits=0)`: Return a new `fixed.Value` with precision changed to `f_bits`, rounding as required.
63-
- Rounding strategy: round to nearest with ties rounded towards positive infinity.
64-
- Under the hood, this involves truncating and adding the most significant truncated bit.
65-
- `.truncate(f_bits=0)`: Return a new `fixed.Value` with precision changed to `f_bits`, truncating as required.
61+
- If `value` is a `fixed.Value`, the precision will be extended or truncated as required.
62+
- `.reshape(f_bits)`: Return a new `fixed.Value` with `f_bits` fractional bits, truncating or extending precision as required.
63+
- `.reshape(shape)`: Return a new `fixed.Value` with shape `shape`, truncating or extending precision as required.
64+
- For example, `value1.reshape(SQ(4, 4))` * value2
6665
- `.__add__(other)`, `.__radd__(other)`, `.__sub__(other)`, `.__rsub__(other)`, `.__mul__(other)`, `.__rmul__(other)`: Binary arithmetic operators.
6766
- If `other` is a `Value`, it'll be cast to a `fixed.Value` first.
6867
- If `other` is an `int`, it'll be cast to a `fixed.Const` first.
@@ -73,8 +72,8 @@ The following operations are defined on it:
7372
- `.__neg__()`, `.__pos__()`, `.__abs__()`: Unary arithmetic operators.
7473
- `.__lt__(other)`, `.__le__(other)`, `.__eq__(other)`, `.__ne__(other)`, `.__gt__(other)`, `.__ge__(other)`: Comparison operators.
7574
- Comparisons between `fixed.Value` of matching size, or between `fixed.Value` and `int` are permitted.
76-
- Comparisons between `fixed.Value` of different widths are not permitted.
77-
- Users are guided by an exception to explicitly `truncate()` or `round()` as needed.
75+
- Comparisons between `fixed.Value` of different `f_bits` are not permitted.
76+
- Users are guided by an exception to explicitly `reshape()` as needed.
7877
- Comparisons between `fixed.Value` and `float` are not permitted.
7978
- Users are guided by an exception to explicitly convert using `fixed.Const` as needed.
8079

@@ -86,7 +85,7 @@ The following additional operations are defined on it:
8685
- `fixed.Const(value, shape=None, clamp=False)`: Create a `fixed.Const` from `value`. `shape` must be a `fixed.Shape` if specified.
8786
- If `value` is an `int` and `shape` is not specified, the smallest shape that will fit `value` will be selected.
8887
- If `value` is a `float` and `shape` is not specified, the smallest shape that gives a perfect representation will be selected.
89-
If `shape` is specified, `value` will be rounded to the closest representable value first.
88+
If `shape` is specified, `value` will be truncated to the closest representable value first.
9089
- If `shape` is specified and `value` is too large to be represented by that shape, an exception is thrown.
9190
- The exception invites the user to try `clamp=True` to squash this exception, instead clamping the constant to the maximum / minimum value representable by the provided `shape`.
9291
- `.as_integer_ratio()`: Return the value represented as an integer ratio `tuple`.
@@ -124,7 +123,7 @@ TBD
124123

125124
- What should we do if a `float` is passed as `other` to an arithmetic operation?
126125
- We could use `float.as_integer_ratio()` to derive a perfect fixed point representation.
127-
However, since a Python `float` is double precision, this means it's easy to make a >50 bit number by accident by doing something like `value * (1 / 3)`, and even if the result is rounded or truncated afterwards, the lower bits can affect rounding and thus won't be optimized out in synthesis.
126+
However, since a Python `float` is double precision, this means it's easy to make a >50 bit number by accident by doing something like `value * (1 / 3)`, and even if the result is truncated afterwards, the lower bits can affect rounding and thus won't be optimized out in synthesis.
128127
- We could use the same width for `other` as for `self`, adjusted to the appropriate exponent for the value.
129128
- We could outright reject it, requiring the user to explicitly specify precision like e.g. `value * Q(15).const(1 / 3)`.
130129
- vk2seb@: I would lean toward outright rejecting this, with an explicit cast necessary (now reflected above).
@@ -135,7 +134,10 @@ TBD
135134
- IEEE 754 defaults to round to nearest, ties to even, which is more expensive to implement.
136135
- Should we make it user selectable?
137136
- We still need a default mode used when a higher precision number is passed to `.eq()`.
138-
- vk2seb@: Both truncation and simple rounding (round to nearest) are commonly used in DSP algorithms. For now, we provide only `truncate()` and `round()` strategies (now reflected above). Additional rounding strategies may be added in a future RFC, however we will always need a default rounding strategy.
137+
- samimia-swks@: In most DSP applications, simple truncating is done (bit picking, which is equivalent to a floor()) because it's free. I would vote for that being the default behavior at least.
138+
- ld-cd@: (...) Truncate is still a reasonable default for most applications.
139+
- ld-cd@: (...) I think a better approach would be to leave rounding and several other common operations that require platform dependent lowering to a subsequent RFC (...).
140+
- vk2seb@: Both truncation and simple rounding (round to nearest) are commonly used in DSP algorithms. For now, we provide only `reshape()` (truncation, now reflected above). Additional rounding strategies may be added in a future RFC, however we will always need a default rounding strategy, and truncation seems like a sane default.
139141

140142
- Are there any other operations that would be good to have?
141143
- From ld-cd@: `min()`, `max()` on `fixed.Shape` (vk2seb@: agree, heavily use this)
@@ -163,9 +165,7 @@ TBD
163165
- vk2seb@: The existing modifications address this:
164166
- Library name: `lib.fixed`
165167
- Type names and shapes: signature has now been updated to use `i_bits`, `f_bits` and the explicit underlying storage in the constructor for `fixed.Shape`.
166-
- We now have both `.round()` and `.truncate()`. I don't think using the same name for increasing and decreasing precision is so bad. But if you feel strongly about this we may consider:
167-
- Renaming them.
168-
- Disallowing increasing precision with these methods, and add a new method for precision extension .
168+
- We now have `.reshape()`, which better represents increasing and decreasing precision. However, I'm open to new names.
169169

170170
## Future possibilities
171171
[future-possibilities]: #future-possibilities

0 commit comments

Comments
 (0)