Perhitungan nyata jarang beruntung pada satu tipe data: array stok int32 harus dijumlahkan dengan hasil perhitungan diskon float32, data sensor integer bertemu rata-rata float64, dan hasilnya wajib punya satu dtype yang dipilih NumPy. Proses pemilihan itu mengikuti aturan promosi tipe (type promotion) — kaidah yang menentukan dtype hasil dari operasi antar tipe berbeda. Tanpa memahami kaidahnya, kita hanya bisa menebak mengapa hasil int32 + float32 ternyata float64, bukan float32.

Bab ini membahas empat alat pemeriksa tipe dari NumPy 2.x:

  • np.result_type dan np.promote_types untuk memprediksi dtype hasil;
  • np.can_cast untuk memeriksa kebolehan konversi dengan mode casting;
  • np.common_type untuk mencari tipe floating bersama;
  • aturan NEP 50 tentang scalar Python yang tidak lagi menaikkan dtype array.

Konsep dasarnya melengkapi praktik konversi manual pada Chapter Konversi Tipe Data dan strategi pemilihan tipe pada Memilih Tipe Data yang Tepat.

result_type: memprediksi dtype tanpa menghitung #

np.result_type menerima array, dtype, atau scalar Python, lalu mengembalikan dtype yang akan dipilih NumPy bila semuanya dipromosikan bersama. Fungsi ini tidak menjalankan operasi apa pun — hanya membaca kaidah — sehingga aman dipakai sebelum array besar dibuat:

python
import numpy as np

kombinasi = [
    (np.int16, np.int32),
    (np.int64, np.float64),
    (np.float32, np.float64),
    (np.int32, np.bool_),
]
for t1, t2 in kombinasi:
    print(f"{t1.__name__} + {t2.__name__} -> {np.result_type(t1, t2).name}")

Output yang Dihasilkan:

text
int16 + int32 -> int32
int64 + float64 -> float64
float32 + float64 -> float64
int32 + bool -> int32

Hasilnya dirangkum dalam tabel kaidah berikut:

KombinasiHasilKaidah yang bekerja
int16 + int32int32integer dengan kapasitas lebih besar menang
int64 + float64float64floating selalu menang atas integer
float32 + float64float64presisi lebih tinggi menang
int32 + boolint32bool diperlakukan sebagai 0/1, integer menang
  1. Baris 3-8 (list dan loop): setiap pasangan dtype dikirim ke np.result_type, dan .name menghasilkan nama dtype untuk dicetak rapi.
  2. Baris 9 (print(...)): baris int32 + bool -> int32 menunjukkan bahwa bool adalah anggota keluarga integer terkecil; operasi nilai + True tetap dihitung dalam tipe integer lawannya.

promote_types dan can_cast #

np.promote_types adalah np.result_type untuk sepasang dtype saja, sedangkan np.can_cast menjawab pertanyaan berbeda: bolehkah tipe A dikonversi ke tipe B? Parameter casting mengatur standar kebolehannya — casting="safe" berarti konversi tidak akan mengubah nilai, sedangkan casting="unsafe" mengizinkan konversi meski presisi atau rentang nilainya terpotong.

python
import numpy as np

print(np.can_cast(np.int64, np.float64, casting="safe"))
print(np.can_cast(np.float64, np.int64, casting="safe"))
print(np.can_cast(np.float64, np.int64, casting="unsafe"))
print(np.promote_types(np.float32, np.float64))

Output yang Dihasilkan:

text
True
False
True
float64
  1. Baris 3 (int64 → float64): NumPy menilai konversi ini safe karena rentang float64 mencakup int64. Perlu diingat: presisi int64 di atas 2⁵³ tetap dapat berkurang, topik yang dibedah pada Chapter Penanganan Overflow dan Presisi.
  2. Baris 4 (float64 → int64): pemeriksaan safe menolak, karena bagian desimal pasti hilang.
  3. Baris 5 (casting="unsafe"): pemeriksaan kini mengembalikan True. Mode unsafe hanya melonggarkan pemeriksaan — keputusan memotong nilai ada di tangan kita, persis seperti perilaku default astype.
  4. Baris 6 (np.promote_types): pasangan float32 dan float64 dipromosikan ke float64.

common_type: tipe floating bersama #

np.common_type menerima beberapa array dan mengembalikan tipe floating (atau complex) paling rendah yang mampu menampung semuanya. Seluruh masukan integer otomatis diangkat minimal ke float64:

python
import numpy as np

a = np.array([1, 2, 3], dtype=np.int32)
b = np.array([0.5, 1.5, 2.5], dtype=np.float32)
print(np.result_type(a, b))
print(np.common_type(a, b).__name__)
print((a + b).dtype)

Output yang Dihasilkan:

text
float64
float64
float64
  1. Baris 4 (np.result_type(a, b)): array int32 bertemu array float32 — hasilnya float64, bukan float32. Alasannya: mantissa float32 hanya 24 bit, tidak cukup menampung semua bilangan int32 secara utuh.
  2. Baris 5 (np.common_type(a, b).__name__): common_type mengembalikan class dtype (misalnya numpy.float64), sehingga kita cetak lewat .name agar outputnya bersih. Ingat juga: argumennya harus array atau scalar, bukan dtype.
  3. Baris 6 ((a + b).dtype): hasil operasi nyata cocok dengan prediksi — float64.

NEP 50: scalar Python tidak menaikkan dtype array #

Kaidah terpenting NumPy 2.x bernama NEP 50. Intinya sederhana: scalar Python (int, float, complex) berstatus weak — lemah — sehingga tidak ikut menentukan dtype hasil selama nilainya muat di dtype array. Jika nilainya tidak muat, NumPy menolak dengan error, bukan diam-diam menaikkan dtype. Perilaku lama NumPy 1.x yang menyesuaikan dtype dengan mempertimbangkan nilai scalar sudah ditinggalkan; perbandingan lengkapnya ada pada Chapter Migrasi NumPy 1.x ke 2.x.

python
import numpy as np

stok = np.array([100, 250, 400], dtype=np.int32)
print((stok + 5).dtype)
try:
    print(stok + 10**12)
except OverflowError as e:
    print("OverflowError:", e)
print((stok + 2.5).dtype)
print(stok + 1e100)

Output yang Dihasilkan:

text
int32
OverflowError: Python integer 1000000000000 out of bounds for int32
float64
[1.e+100 1.e+100 1.e+100]
  1. Baris 4 ((stok + 5).dtype): scalar 5 muat di int32, maka dtype tetap int32 — bukti bahwa scalar Python tidak menaikkan dtype array.
  2. Baris 5-8 (try/except): 10**12 melampaui batas int32 (±2,1 miliar). NumPy 2.x memunculkan OverflowError alih-alih mengubah dtype menjadi int64 secara diam-diam.
  3. Baris 9 ((stok + 2.5).dtype): scalar 2.5 bertipe float memicu kaidah integer + float, sehingga hasilnya float64.
  4. Baris 10 (stok + 1e100): 1e100 besar tetapi muat di float64, jadi tidak ada error; nilai array 100, 250, 400 menjadi tak berpengaruh setelah dijumlahkan ke 1e100, sehingga cetakannya 1.e+100 semua.

Tabel berikut merangkum perilaku scalar Python pada NumPy 2.x untuk array int32:

EkspresiHasilPenjelasan
stok + 5int32scalar lemah; nilai muat
stok + 10**12OverflowErrornilai di luar batas int32
stok + 2.5float64kaidah integer + float
stok + 1e100float64nilai besar muat di float64

Prinsip yang sama berlaku pada array float: float32 dijumlahkan dengan 0.5 tetap float32, karena scalar float Python tidak menuntut kenaikan ke float64.

Aturan promosi yang kita kuasai di sini adalah sisi teori dari konversi eksplisit astype pada Chapter Konversi Tipe Data, dan menjadi dasar pertimbangan ketika memilih dtype hemat memori pada Memilih Tipe Data yang Tepat. Untuk dampak batas nilai dan presisi terhadap hasil hitung — termasuk kasus int64 ke float64 di atas — lanjutkan ke Penanganan Overflow dan Presisi.