Data types (dtypes)

NumPy · Essentials · 12 min

Picking and checking dtypes avoids silent overflow, wasted memory and surprising type promotion.

The dtype says what each element is: int8...int64, unsigned uint8...uint64, float16/float32/float64, complex128, bool, plus fixed-width strings and object. NumPy infers it from your data ([1, 2] gives the platform default integer, which since NumPy 2 is int64 on all 64-bit systems including Windows; [1, 2.5] gives float64) or you pass dtype= explicitly.

a.astype(new_dtype) converts and always returns a new array. Converting floats to ints truncates toward zero; it does not round.

Fixed-size integers can overflow. Array arithmetic wraps around silently: an int8 holding 127 plus 1 becomes -128. NumPy 2 made Python scalars "weak" (NEP 50): in arr + 3 the array's dtype wins, so uint8_array + 1 stays uint8. And if the Python value cannot fit, as in uint8_array + 300, NumPy now raises OverflowError instead of quietly upcasting.

When two arrays meet, NumPy promotes to a type that can hold both: int64 + float64 gives float64, int8 + int16 gives int16. np.result_type(a, b) tells you what you'll get.

Memory is a dtype decision: a million float32 values take 4 MB, float64 takes 8 MB. Use smaller types when the precision is enough and memory or bandwidth matters (images in uint8, ML weights in float32).

Strings in classic NumPy are fixed-width (<U5 means Unicode, at most 5 characters), and longer values assigned later get truncated. NumPy 2 adds np.dtypes.StringDType() for variable-length strings.

Example

import numpy as np

a = np.array([1, 2, 3])
print(a.dtype)                       # int64
b = np.array([1, 2.5])
print(b.dtype)                       # float64

print(np.array([2.9, -2.9]).astype(int))   # [ 2 -2]  truncates

small = np.array([127], dtype=np.int8)
print(small + np.int8(1))            # [-128]  wrapped around!

pix = np.array([250, 10], dtype=np.uint8)
print((pix + 1).dtype)               # uint8 (Python int is "weak")
print(pix.astype(np.int16) + 10)     # [260  20]  widen first

print(np.result_type(np.int8, np.float32))   # float32
print(np.array(["cat", "horse"]).dtype)       # <U5

Remember

  • One dtype per array; check it with a.dtype, change it with astype
  • Integer arithmetic wraps on overflow, silently
  • In NumPy 2 the array's dtype wins over Python scalars (NEP 50)
  • Smaller dtypes save memory but limit range and precision

Watch out for

  • Assuming astype(int) rounds: it truncates, so use np.round(x).astype(int) if you mean rounding.

Check yourself

  1. `x = np.array([200, 100], dtype=np.uint8)`. What is `x + x`?
  2. Under NumPy 2 rules, what dtype is `np.ones(3, dtype=np.float32) * 2.5`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: numpy.org/doc/stable/user/basics.types.html