53 / 63 · Concept
Fixed-point arithmetic, rounding and saturation
Specify numeric ranges with W,F and design guard/sticky ties-to-even rounding followed by saturation.
Lessons are free to read. Enroll to save your learning progress.
Prerequisites: signed addition/multiplication widths, arithmetic right shifts and overflow.
Agree on the binary-point position
Fixed-pointFixed-point A number representation with a predetermined binary-point position, applying a fixed scale to an integer bit pattern. Specify width, signedness and the number of fractional bits together. interprets an integer X as . No binary-point bit is stored in hardware. Here, total signed width W includes the sign bit, and F is the fractional-bit count. Q notation varies between references in whether the integer width includes the sign, so specifying W,F together makes an interface clearer.
For W=8,F=4, spacing is 1/16 and the range is -8 to 7.9375. Under this contract 0x18 represents 1.5, not integer 24; with F=3 it represents 3. Connecting wires does not ensure matching numeric meanings, so document each port's scale.
Align binary points before addition. With unequal F, widen and left-shift the smaller-F operand, or reduce the larger-F operand using a defined rounding rule. An exact sum of two equal-W,F operands needs W+1 bits with the same F. Multiplication multiplies raw integers and adds fractional-bit counts.
The full product of two eight-bit F=4 values is signed 16-bit F=8. Returning to eight-bit F=4 requires removing four fractional bits, then checking the signed eight-bit range. Truncating the product to eight bits first destroys the magnitude needed for later saturation.
Truncation and rounding differ
Arithmetic right shift discarding d bits gives , rounding negative values downward too. With , the exact quotient is -1.5 but q=-2, unlike truncation toward zero. Selecting high bits does not have the same error behavior for negative values as for positive values.
Design round-to-nearest, ties-to-even: choose the closest integer, and the even one at an exact midpoint. Let the highest discarded bit be guard g, the OR of remaining discarded bits sticky t, and the least significant retained bit of q be l.
For g=0, the fraction is below half, so leave q unchanged. For g=1,t=1 it exceeds half, so increment. For g=1,t=0 it is exactly half; add 1 only if q is odd. The same rule works for negatives because the floor quotient and remainder always satisfy .
For example, -24/16=-1.5 rounds to q=-2, already even; -40/16=-2.5 has odd q=-3 and rounds up to -2. A separate rule such as “always subtract 1 for negatives” is incorrect. Without saturation, final error is at most half an output LSBLSB The bit with the lowest place value. Check both the notation direction and the indexing rules. Learn more. Ties-to-even does not guarantee zero mean error for every distribution, but reduces bias from always sending ties in one direction.
Below, raw P=2040 in 16-bit F=8 converts to eight-bit F=4. The raw quotient 127.5 rounds to even 128, then clips to the signed eight-bit maximum 127. Preserve a wide representation until final storage.
- Input P=2040: 16 bits · F=8
- Reduce scale: 2040 / 16 = 127.5
- ties-to-even: Rounded raw = 128
- Saturate, then store: raw 127 → 7.9375
Saturate after rounding
Saturation clips above-maximum results to the maximum and below-minimum results to the minimum. Unlike wraparound, it does not turn a large positive value negative. Correct saturation cannot be recovered after narrowing has already wrapped the value.
This complete combinational module converts signed 16-bit F=8 to signed eight-bit F=4. With d=4, guard=p[3] and sticky is the OR of p[2:0]. Rounding increment and range comparisons use 17-bit signed values; only the final step takes eight bits.
module requantize (
input logic signed [15:0] p,
output logic signed [7:0] y,
output logic saturated
);
logic signed [16:0] wide, q, rounded;
logic inc;
always_comb begin
wide = {p[15], p};
q = wide >>> 4;
inc = p[3] & ((|p[2:0]) | q[0]);
rounded = q + (inc ? 17'sd1 : 17'sd0);
saturated = 1'b0;
if (rounded > 17'sd127) begin
y = 8'sh7f;
saturated = 1'b1;
end else if (rounded < -17'sd128) begin
y = 8'sh80;
saturated = 1'b1;
end else begin
y = rounded[7:0];
end
end
endmoduleFor P=2040, P/16=127.5 rounds to 128 and saturates to 127, representing 7.9375. Checking only pre-round q=127 misses the overflow. saturated means the value exceeded range and was limited, not merely that fractional bits were discarded.
In a parameterized version, d=0 has no discarded bits and d=1 has no sticky region. Hiding an out-of-range part-select inside a conditional may not prevent an elaboration error.
Rounding and saturation add adder, comparator and MUXMultiplexer A combinational circuit that connects one of several inputs to the output according to a select signal. Specify how select bits map to input numbers. Learn more cost and delay, and are separate policy choices. AMD's fixed-point format documentation makes this distinction too. When accumulating multiple terms, first decide whether to retain intermediate width and convert only once at the end. Saturating every step can break associativity; rounding every step can accumulate small errors repeatedly.
Try it yourself
Convert signed 16-bit F=8 input P to signed 8-bit F=4. For P=24,40,−24,−40,2040, find floor quotient q, guard/sticky, rounded integer, final raw output and represented value. What goes wrong if saturation is checked first for 2040?
Read the explanation
Every input has low bits 1000, hence guard=1, sticky=0. P=24: q=1 (odd)→2, raw=2, value=0.125. P=40: q=2 (even)→2, raw=2, value=0.125. P=−24: q=−2 (even)→−2, raw=−2, value=−0.125. P=−40: q=−3 (odd)→−2, raw=−2, value=−0.125. P=2040: q=127 (odd)→128, beyond the signed eight-bit maximum, so raw=127, value=7.9375, saturated=1. Checking only q=127 and then adding 1 in eight bits can wrap to 0x80 and output −8. Complete increment and comparison in a wider signed representation.