NumPy: dtype Data Types
Last updated: 2026-08-26
dtype Data Types
1. What You'll Learn
- The dtype system and classification
- Creating arrays with specific dtypes
- Type conversion and overflow risks
- Structured dtypes
- The memory/precision/speed trade-off of dtype
2. Story
Alice's model accuracy dropped from 95% to 12% overnight. The culprit: converting float64 to int8 overflowed all values above 127 into negatives. "dtype isn't a minor detail — pick the wrong type and your data is ruined."
3. Key Concepts
(1) The dtype System
NumPy's dtype (data type) describes the type of every element in an array. Every ndarray has a .dtype attribute.
np.int32 # 32-bit signed integer
np.float64 # 64-bit floating point
np.bool_ # boolean
np.str_ # unicode string
np.object_ # Python object
dtype has two fields:
| Field | Meaning | Example |
|---|---|---|
kind |
Type category | 'i'=int, 'f'=float, 'u'=uint, 'b'=bool |
itemsize |
Bytes per element | 4 for int32 |
import numpy as np
a = np.array([1, 2, 3])
print(a.dtype) # int64 (on 64-bit platforms)
print(a.dtype.kind) # 'i'
print(a.dtype.itemsize) # 8
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(2) Integer Type Comparison
| Type | Range | Bytes | Alias |
|---|---|---|---|
int8 |
-128 ~ 127 | 1 | — |
int16 |
-32768 ~ 32767 | 2 | — |
int32 |
-2147483648 ~ 2147483647 | 4 | intp (32-bit) |
int64 |
-9223372036854775808 ~ 9223372036854775807 | 8 | intp (64-bit) |
uint8 |
0 ~ 255 | 1 | — |
uint16 |
0 ~ 65535 | 2 | — |
uint32 |
0 ~ 4294967295 | 4 | — |
uint64 |
0 ~ 18446744073709551615 | 8 | — |
(3) Float Type Comparison
| Type | Bytes | Precision | Max | Min Positive |
|---|---|---|---|---|
float16 |
2 | ~3 digits | 65504 | 5.96e-8 |
float32 |
4 | ~7 digits | 3.4e38 | 1.18e-38 |
float64 |
8 | ~15 digits | 1.8e308 | 2.23e-308 |
float128 |
16 | ~33 digits | 1.19e4932 | 3.36e-4932 |
float128 on many platforms is actually 80-bit extended precision padded to 128 bits, not true 128-bit.
(4) Specifying dtype
import numpy as np
# By NumPy type object
a = np.array([1, 2, 3], dtype=np.float32)
# By string
b = np.array([1, 2, 3], dtype='float32')
# By single-character code
c = np.array([1, 2, 3], dtype='f4') # float32: 'f' + 4 bytes
| Character | Type | Example |
|---|---|---|
b |
boolean | np.dtype('b') |
i |
signed int | np.dtype('i4') = int32 |
u |
unsigned int | np.dtype('u1') = uint8 |
f |
float | np.dtype('f8') = float64 |
c |
complex | np.dtype('c16') = complex128 |
S |
byte string | np.dtype('S10') = 10-char string |
U |
unicode string | np.dtype('U5') = 5-char unicode |
(5) Type Conversion and Overflow
import numpy as np
# astype returns a new array with the target dtype
a = np.array([1, 2, 3], dtype=np.int32)
b = a.astype(np.float64) # [1. 2. 3.]
# Overflow: values beyond the target range wrap around
c = np.array([100, 200, 300], dtype=np.uint8)
# [100, 200, 44] — 300 - 256 = 44
# Float to int truncates
d = np.array([1.9, 2.5, 3.1])
print(d.astype(np.int32)) # [1 2 3]
Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (
pip install numpy) and compare. Actual values may vary by NumPy version and random seed.
(6) Structured dtypes
For tabular data with mixed types, use structured dtypes:
import numpy as np
# Define a structured dtype
dt = np.dtype([('name', 'U10'), ('age', 'i4'), ('salary', 'f8')])
# Create array with structured dtype
employees = np.array([
('Alice', 30, 75000.0),
('Bob', 25, 62000.0),
('Charlie', 35, 85000.0)
], dtype=dt)
print(employees['name']) # ['Alice' 'Bob' 'Charlie']
print(employees['salary'].mean()) # 74000.0
Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (
pip install numpy) and compare. Actual values may vary by NumPy version and random seed.
▶ Example
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: Overflow demonstration (Difficulty ⭐⭐)
import numpy as np
# int8 range: -128 to 127
vals = np.array([100, 120, 127, 128, 130, 200], dtype=np.int8)
print("int8 values:", vals)
# [100 120 127 -128 -126 -56]
# uint8 range: 0 to 255
vals_u = np.array([200, 255, 256, 300], dtype=np.uint8)
print("uint8 values:", vals_u)
# [200 255 0 44]
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
▶ Example
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: Structured dtype — employee records (Difficulty ⭐⭐)
import numpy as np
dt = np.dtype([('name', 'U10'), ('dept', 'U15'), ('salary', 'f8'), ('years', 'i2')])
data = np.array([
('Alice', 'Engineering', 95000, 5),
('Bob', 'Marketing', 72000, 3),
('Charlie', 'Engineering', 110000, 8),
('Diana', 'Sales', 68000, 2),
], dtype=dt)
# Query by department
eng = data[data['dept'] == 'Engineering']
print("Engineering avg salary:", eng['salary'].mean())
# Sort by salary
sorted_data = np.sort(data, order='salary')
print("Top earner:", sorted_data[-1]['name'], sorted_data[-1]['salary'])
> **Output:** Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
4. Comparison Tables
(1) Type Casting Rules
| Source ↓ / Target → | int | float | complex | string |
|---|---|---|---|---|
| int | ✅ | ✅ (safe) | ✅ (safe) | ✅ (decimal) |
| float | ⚠️ truncates | ✅ | ✅ (safe) | ✅ (decimal) |
| complex | ⚠️ loses imag | ⚠️ loses imag | ✅ | ✅ (string) |
| bool | ✅ (0/1) | ✅ (0.0/1.0) | ✅ | ✅ |
(2) Memory vs Precision Matrix
| dtype | 1M elements | Relative to float64 | Use case |
|---|---|---|---|
| float16 | 2 MB | 25% | GPU training, storage |
| float32 | 4 MB | 50% | Deep learning, most ML |
| float64 | 8 MB | 100% | Scientific computing |
| int8 | 1 MB | 12.5% | Image bytes, flags |
| int32 | 4 MB | 50% | General integer |
| int64 | 8 MB | 100% | Large indices, timestamps |
(3) Overflow Behavior
| Operation | int8 | uint8 | float32 |
|---|---|---|---|
| Max + 1 | -128 (wrap) | 0 (wrap) | inf |
| Min - 1 | 127 (wrap) | 255 (wrap) | -inf |
| 0 / 0 | error | error | nan |
5. Principle Diagram
graph TB
A[Python object] --> B{dtype inference}
B -->|int| C[int64]
B -->|float| D[float64]
B -->|mixed| E[upcast to common type]
B -->|explicit| F[user-specified dtype]
C --> G[ndarray with fixed dtype]
D --> G
E --> G
F --> G
G --> H[vectorized operations]
G --> I[memory-efficient storage]
style A fill:#e1f5fe
style G fill:#c8e6c9
style H fill:#fff9c4
style I fill:#fff9c4
▶ Example: dtype conversion and memory usage (Difficulty ⭐⭐)
import numpy as np
# Compare memory usage of different dtypes
arr_int64 = np.ones(1000000, dtype=np.int64)
arr_float32 = np.ones(1000000, dtype=np.float32)
arr_int8 = np.ones(1000000, dtype=np.int8)
print(f"int64: {arr_int64.nbytes / 1e6:.1f} MB")
print(f"float32: {arr_float32.nbytes / 1e6:.1f} MB")
print(f"int8: {arr_int8.nbytes / 1e6:.1f} MB")
# Convert between types
converted = arr_float32.astype(np.int32)
print(f"Converted dtype: {converted.dtype}")
Output:
TEXT 📖 Display onlyint64: 8.0 MB float32: 4.0 MB int8: 1.0 MB Converted dtype: int32
❓ FAQ
int64 on 64-bit platforms, int32 on 32-bit. NumPy chooses based on your system architecture. You can always specify explicitly with dtype=np.int32.int8(127) + 1 = -128. This is a common source of bugs. Use np.seterr(over='warn') to catch it.float and double.object dtype (which stores Python object references, losing performance).📖 Summary
- dtype defines the element type of every ndarray, with
kind(category) anditemsize(bytes) fields - Integer types: int8/16/32/64, uint8/16/32/64 — overflow wraps silently
- Float types: float16/32/64/128 — more bytes = more precision + more memory
- Specify dtype via
np.dtype(), type objects, strings, or single-character codes astype()converts between types; overflow wraps, float-to-int truncates- Structured dtypes enable mixed-type tabular data within a single ndarray
📝 Exercises
-
Beginner (Difficulty ⭐): Create arrays of int8, int16, int32, int64, float32, float64 — each with 1000 elements of value 1. Print their
nbytesand explain the memory differences. -
Intermediate (Difficulty ⭐⭐): Create a uint8 array with values [0, 127, 128, 255, 256, -1]. Observe which values overflow and explain the results. Then create a structured dtype for a book catalog with fields: title (string), author (string), year (int16), price (float32).
-
Advanced (Difficulty ⭐⭐⭐): Convert a float64 array of 1 million random numbers to float32, then back to float64. Compute the mean absolute error between the original and the round-tripped array. Repeat with float16. Record the precision loss for each type.