GPU Lab / interactive book
GPU mapSource ↗

CHAPTER 10

What changes when matrix inputs use fewer bits?

Separate input rounding from accumulation, then explain how this connects to cooperative matrix hardware.

Start with the operation

PyTorch

import torch

row = torch.tensor([1.0003 if k < 8 else -1.0 for k in range(16)],
                   dtype=torch.float32, device="cuda")
a = row.repeat(16, 1).to(torch.float16)
b = torch.ones((16, 16), dtype=torch.float16, device="cuda")
c = a @ b
print(c[0, 0].float().item())
Desktop setup

A matrix multiplication in a neural network can store inputs in a smaller number format. torch.float16 stores each input in two bytes instead of FP32’s four. The conversion with .to(...) may round a value before @ uses it. Converting the result to FP32 for printing cannot restore discarded input information.

row.repeat(16, 1) makes A from sixteen copies of the row; torch.ones makes B entirely ones. Every output is therefore the same row–column dot product. The row starts with eight values written as 1.0003 and ends with eight −1 values. They first enter FP32 storage, then convert to FP16. .float() converts the selected result to FP32 and .item() reads its single value for printing. You choose the inputs and format; PyTorch chooses the matrix implementation. The browser uses a declared arithmetic order to isolate conversion and accumulation.

Run the examples below in your browser with no installation or GPU required. The browser simulates the calculation; it does not execute PyTorch or GPU code. To run the PyTorch snippet itself, use a Python environment with PyTorch and an NVIDIA GPU.

Idea 1 of 3

Inspect what reaches the multiplication

Definitions

Number format
A rule for representing numbers with a fixed set of bits. FP32 uses 32 bits, while FP16 and BF16 use 16.
Rounding
Replace a number by a nearby value that the chosen format can represent.
Fraction precision
How finely a format distinguishes nearby numbers at a given magnitude.
Range
The magnitudes a format can represent, from tiny non-zero values to large values.

FP16 distinguishes fewer nearby values than FP32. BF16 has fewer fraction bits than FP16, but a much wider range, similar to FP32. The two small formats therefore make different trade-offs. In this first experiment, keep the near-one inputs fixed and inspect the value before and after conversion.

Indices begin at zero: positions 0–7 contain the near-one input and 8–15 contain −1. Stepping selects an input position. B’s corresponding input is exactly one in every supported format.

Model assumptions: conversion rounds to the nearest representable value, with ties to an even last bit, including tiny values near zero. The trace is a CPU teaching calculation, with no hardware execution, performance or tensor-core mapping.

Try it

Predict which of the 16 row values change in FP16. Run and compare original and converted values. Change only the input format to BF16, then FP32; explain why −1 behaves differently from the near-one value.

Inspect the PyTorch example for these settings

PyTorch

import torch

row = torch.tensor([1.0003 if k < 8 else -1.0 for k in range(16)],
                   dtype=torch.float32, device="cuda")
a = row.repeat(16, 1).to(torch.float16)
b = torch.ones((16, 16), dtype=torch.float16, device="cuda")
c = a @ b
print(c[0, 0].float().item())
Desktop setup

Predict the result, then press Run.

The input conversion happens before accumulation. Once a converted input has become exactly 1, no later addition can recover its original small excess.

Idea 2 of 3

Add products into an FP32 running sum

Definitions

FP32 accumulator
A running sum stored and rounded in FP32 after each addition in this model.
Cancellation
Adding values with opposite signs can leave a result much smaller than the individual inputs.

Each product here is the converted A input times one. Compare two FP32 running sums: one receives converted inputs; the reference receives original FP32 inputs. In the near-one pattern, eight negative ones cancel most of the positive sum. A small discarded input difference can then dominate the final answer.

Next try the small positive inputs to test range. Finally use alternating 1 and −0.5 to test values represented exactly in all three formats. The complete PyTorch example below uses an explicit loop on the CPU to match the model’s chosen order; it is an arithmetic explanation, not a GPU matrix implementation.

Model assumptions: each addition rounds to FP32 and K advances from 0 to 15. All A rows are identical and all B entries are one, so this one trace explains every C entry. Actual matrix kernels may use a different accumulation order or numeric rules.

Try it

Predict whether FP32 accumulation recovers the near-one input difference after FP16 conversion. Run, then step across K = 7 and K = 15 to compare sums before and after cancellation. Change only the pattern to small positive values and compare FP16 with BF16. Use the exact pattern as a final control.

Inspect the PyTorch example for these settings

PyTorch

import torch

row = torch.tensor([1.0003 if k < 8 else -1.0 for k in range(16)],
                   dtype=torch.float32)
converted = row.to(torch.float16).float()
total = torch.zeros((), dtype=torch.float32)
reference = torch.zeros((), dtype=torch.float32)
for k in range(16):
    total = total + converted[k] * 1.0
    reference = reference + row[k] * 1.0
print("Converted-input sum:", total.item())
print("Original-input sum:", reference.item())
Desktop setup

Predict the result, then press Run.

Agreement on the exact pattern does not establish accuracy for every input. A useful accuracy check needs the values and formats that the real workload uses, as well as an appropriate reference.

Idea 3 of 3

Connect the arithmetic to tensor cores

Definitions

Tensor core
Specialised GPU hardware for supported matrix multiply-and-accumulate operations.
Multiply–accumulate
Multiply inputs and add their products to an existing output sum.

The previous chapter grouped outputs into tiles to reuse inputs. Tensor-core implementations also work on matrix groups: threads cooperate to supply input tiles and retain or store the output tile. On NVIDIA GPUs, the warp introduced earlier is one such cooperation group. A lane does not independently perform an entire matrix multiplication.

A 16 × 16 output tile with 16 K positions expresses 4,096 scalar multiply–accumulates. That describes arithmetic work, not hardware instructions, cycles or speed. Supported shapes, formats and cooperation rules depend on the GPU and programming interface.

The browser’s sequential additions do not execute or simulate tensor cores. For a @ b, PyTorch and its selected library implementation decide whether and how to use specialised hardware. A smaller dtype alone does not establish the implementation or its speed. The numeric exercise gives you an accuracy question to ask alongside a separate hardware measurement.

Try it

Explain two independent choices to a colleague: how the inputs are represented, and how products are accumulated. Then explain why the sequential browser trace cannot reveal which tensor-core instructions PyTorch selected.

Try a new case

Apply it

A new row alternates 1 and −0.5, and B remains all ones. Predict whether converting the inputs to FP16 changes the result of this model. Does that result prove that near-one inputs or very small inputs are safe too?

Compare your answer
Both 1 and −0.5 are exactly representable in FP16. 0 row inputs change and both sequential sums are 4. This tests those inputs only; cancellation and small magnitudes can expose different conversion errors. It supplies no tensor-core execution or timing evidence.

References

Use these official references to check the concepts and code on this page.

These browser exercises do not run an installed PyTorch or CUDA toolkit. For optional local execution, choose documentation matching your installed tools. Links open online in a new tab; the questions and answer comparisons below work offline.

Use the documentation

Use the references to explain each answer, then apply it to this page’s example. No installation is needed.

Install GPU Lab

Give GPU Lab its own icon and window. All lessons and simulators work offline once the course is saved.

Use your browser’s install option:

If your browser has no install option, try Chrome or Edge.

App status

Version

Checking for updates

Offline access

Not saved for offline use

Save all lessons, browser simulators, diagrams and example files. Desktop execution needs separate PyTorch or CUDA/GPGPU-Sim tools; their installations are not part of this offline download. External links need internet access.