Skip to content

← Dtypes and Numerics step 1 of 6

Medium Primitives

What dtype comes out

Mixing dtypes in one operation is legal and the result’s dtype follows rules that are mostly obvious and have one genuine surprise in them.

Between tensors: the wider one wins

int32   + float32   ->  float32     float beats int
float16 + float32   ->  float32     wider float wins
bool    + int8      ->  int8        bool is the narrowest of all

Categories rank bool < integer < floating < complex, and within a category the wider wins.

Between a tensor and a Python number: the tensor wins

This is the surprise.

int64 tensor + 1.5     ->  float32       not float64
int32 tensor + 2       ->  int32         not int64

A Python scalar is weak. It participates in the category ranking, so adding a float to an integer tensor does move you to floating point, but it does not drag you to the widest type in that category. You get the default floating dtype, which is float32.

If Python scalars promoted normally, x + 1.5 on a float16 tensor would silently produce float32 and every mixed-precision model would fall out of half precision on the first bias add. The weak rule is what stops that:

float16 tensor + 1.5                  ->  float16     stays half
float16 tensor + torch.tensor([1.5])  ->  float32     widened

A zero-dimensional tensor is weak too

int64 + torch.tensor(1.5)      0-dim, weak    ->  float32
int64 + torch.tensor([1.5])    1-dim, strong  ->  float32
float16 + torch.tensor(1.5)    0-dim, weak    ->  float16
float16 + torch.tensor([1.5])  1-dim, strong  ->  float32

The rule is about rank, not about being a Python object. A 0-dim tensor is treated as a scalar for promotion, which is usually what you want and is worth knowing before it surprises you: loss * scale behaves differently depending on whether scale is torch.tensor(2.0) or torch.tensor([2.0]).

Why you should care

Silent widening is a memory and speed regression that no test catches: the numbers are right and the tensor is twice the size. Silent narrowing is worse, because it is a correctness bug.

When it matters, say what you want:

x.to(torch.float32)
torch.tensor(1.5, dtype=torch.float64)

Your task

def result_dtype(a_dtype: str, b_dtype: str) -> str

Both arguments name a dtype, except that "pyfloat" and "pyint" mean a plain Python number rather than a tensor. Return the name of the dtype the addition produces, without the torch. prefix.

Build the operands and ask. That is the intended solution.