Two limits of a digital record
Until now, we selected the sampling instants and used ideal numbers for the sample values. A real analog-to-digital converter (ADC) also limits the precision in amplitude:
- Sampling selects the time instants .
- Quantization replaces the measured value with one level from a finite set of levels and gives the related digital code.
Resolution is the number of bits in the code. More bits let you tell apart amplitude values that are nearer to each other, but they do not correct the aliasing from the previous lessons. Here we change the resolution and keep the sample rate the same.
A converter model with positive and negative levels
We measure the signal in arbitrary units: the bottom of the scale is , and the top is . For a resolution of bits, there are codes. In the selected model, the step between levels is
With , we get eight signed codes and a step of :
| Code | Quantized value |
|---|---|
| −4 | −1.00 |
| −3 | −0.75 |
| −2 | −0.50 |
| −1 | −0.25 |
| 0 | 0.00 |
| 1 | 0.25 |
| 2 | 0.50 |
| 3 | 0.75 |
This model has no level. This comes from the range of signed integers that we selected: from to . This convention agrees with two’s complement, which we discuss in more detail when we move to fixed-point. A specific ADC can have a different code format.
From value to code and back
For each input value , do three steps:
- Divide by the step .
- Round to the nearest integer.
- If the result is outside the code range, replace it with the nearest limit of the code range.
As a formula:
Here round means rounding, and clip means limiting the range. The script uses np.rint: a value exactly in the middle between two integers is rounded to the nearest even integer.
To show the value of a code on a plot, multiply the code by the step:
For example, with three bits, the input gives . After rounding, the code is , and the quantized value is . The input gives code and value .
What a digital signal contains
The ADC gives codes. In our example, the code sequence contains the integer 2. On the selected scale, it means the value 0.50.
In Python, you can work with both representations:
codes = np.array([2, 3, -2]) # integer codes
step = 0.25
values = codes * step # [0.50, 0.75, -0.50]
The values array is convenient for plots and for more calculations. Conversion to floating-point numbers does not recover the lost precision: the original values 0.62 and 0.50 could give the same code. To convert normalized values to volts, you also need the input range scale of the device.
Quantization error and range limit
We define the error as the difference between the quantized value and the original value:
If the rounded code is in the valid range, then
If the code had to be limited, this guarantee is lost. For example, for the input , rounding gives code , but the maximum code is . We get the value and the error ; its magnitude is more than half the step, . For the input , the error is even larger.
This limit is clipping (the signal is cut off at a level). In our asymmetric model, clipping can start before : use the valid codes as the reference, not only the nominal limits of the scale.
Quantization error does not always look like random noise. For a periodic signal, it can also be periodic. We examine its spectrum after we learn the DFT.
Lab: resolution and clipping
The script compares models with a resolution of 3 and 8 bits:
uv run labs/01/04-quantization.py --show

The gray line is the original cosine, the blue dots are ideal samples, and the orange dots are the values of the codes. Steps connect the orange dots to make the plot easier to read; the array contains separate samples. The bottom panel shows the error. The script also prints the codes and their values separately.
First compare a few resolutions, then increase the amplitude:
uv run labs/01/04-quantization.py --bits 2 3 4 8 --samples 48 --show
uv run labs/01/04-quantization.py --bits 3 --amplitude 1.2 --show --output-dir /tmp/dsp-clipping
In the second experiment, the peaks hit the limit codes. More bits make the step smaller, but they do not make the selected input scale wider.
What one more bit gives
Each additional bit doubles the number of levels and makes the step two times smaller. When rounding does not need code limiting, the upper limit of the error also becomes two times smaller.
In a real ADC, its own noise and nonlinearity also limit the precision. So the nominal resolution and the actual precision can be different. We keep the quantitative estimates of noise and the effective number of bits (ENOB) for the related part of the course.
Self-check
For the three-bit model, the step is , and the codes are in the range from to . We get the code by rounding to the nearest integer and then limiting the range. The quantized value is .
Find the code and the quantized value for the input .
. The nearest integer is , and it is in the range from to . The quantized value is . The error is — its magnitude is less than half the step, .
Find the code and the quantized value for the input .
. The result is symmetric to the previous one: . The asymmetry of our model shows only at the edges of the range: code exists, but code does not.
For the input , find the code before limiting, the final code, and the error .
, and rounding gives . This code does not exist, so we limit it to : . The magnitude of the error is more than half the step, — this is a sign of clipping.
The code is . Can you say that the original value was exactly ?
Code comes from all inputs for which rounds to — that is, within of . From the code, you know only this interval; the exact original value is lost.