Masalah:
Anda ingin mencari sisa (non-negatif).
Contoh:
Dalam tabel numbers
, Anda memiliki dua kolom bilangan bulat:a
dan b
.
a | b |
---|---|
9 | 3 |
5 | 3 |
2 | 3 |
0 | 3 |
-2 | 3 |
-5 | 3 |
-9 | 3 |
5 | -3 |
-5 | -3 |
5 | 0 |
0 | 0 |
Anda ingin menghitung sisa dari pembagian a
oleh b
. Setiap sisa harus berupa nilai bilangan bulat non-negatif yang lebih kecil dari b
.
Solusi 1 (tidak sepenuhnya benar):
SELECT a, b, a % b AS remainder FROM numbers;
Hasilnya adalah:
a | b | sisa |
---|---|---|
9 | 3 | 0 |
5 | 3 | 2 |
2 | 3 | 2 |
0 | 3 | 0 |
-2 | 3 | -2 |
-5 | 3 | -2 |
-9 | 3 | 0 |
5 | -3 | 2 |
-5 | -3 | -2 |
5 | 0 | kesalahan |
0 | 0 | kesalahan |
Diskusi:
Solusi ini bekerja dengan benar jika a adalah non-negatif. Namun, jika negatif, tidak mengikuti definisi matematika dari sisanya.
Secara konseptual, sisa adalah apa yang tersisa setelah pembagian bilangan bulat a
oleh b
. Secara matematis, sisa dua bilangan bulat adalah bilangan bulat tak negatif yang lebih kecil dari pembagi b
. Lebih tepatnya, ini adalah bilangan r∈{0,1,...,b - 1} yang memiliki beberapa bilangan bulat k sehingga a =k * b + r.
Beginilah cara a % b
bekerja untuk dividen non-negatif di kolom a
:
5 = 1 * 3 + 2
, jadi sisa 5 dan 3 sama dengan 2
.
9 = 3 * 3 + 0
, jadi sisa 9 dan 3 sama dengan 0
.
5 = (-1) * (-3) + 2
, jadi sisa 5 dan -3 sama dengan 2
.
Jelas, kesalahan ditampilkan jika pembagi b
adalah 0
, karena Anda tidak dapat membagi dengan 0
.
Mendapatkan sisa yang benar bermasalah ketika dividen a
adalah bilangan negatif. Sayangnya, a % b
dapat mengembalikan nilai negatif ketika a
adalah negatif. Mis.:
-2 % 5
mengembalikan -2
kapan seharusnya mengembalikan 3
.
-5 % -3
mengembalikan -2
kapan seharusnya mengembalikan 1
.
Solusi 2 (benar untuk semua angka):
SELECT a, b, CASE WHEN a % b >= 0 THEN a % b ELSE a % b + ABS(b) END AS remainder FROM numbers;
Hasilnya adalah:
a | b | sisa |
---|---|---|
9 | 3 | 0 |
5 | 3 | 2 |
2 | 3 | 2 |
0 | 3 | 0 |
-2 | 3 | 1 |
-5 | 3 | 1 |
-9 | 3 | 0 |
5 | -3 | 2 |
-5 | -3 | 1 |
5 | 0 | kesalahan |
0 | 0 | kesalahan |
Diskusi:
Untuk menghitung sisa pembagian apa saja dua bilangan bulat (negatif atau non-negatif), Anda dapat menggunakan CASE WHEN
konstruksi. Jika a % b
non-negatif, sisanya hanya a % b
. Jika tidak, kita perlu memperbaiki hasil yang dikembalikan oleh a % b
.
Jika a % b
mengembalikan nilai negatif, Anda harus menambahkan nilai absolut pembagi ke a % b
. Yaitu, buat a % b + ABS(b)
:
-2 % 5
mengembalikan -2
kapan seharusnya mengembalikan 3
. Anda dapat memperbaikinya dengan menambahkan 5
.
-5 % (-3)
mengembalikan -2
kapan seharusnya mengembalikan 1
. Anda dapat memperbaikinya dengan menambahkan 3
.
Ketika a % b
mengembalikan nilai negatif, CASE WHEN
hasilnya harus a % b + ABS(b)
. Ini adalah bagaimana Anda mendapatkan Solusi 2. Jika Anda membutuhkan penyegaran tentang bagaimana ABS()
berfungsi, lihat buku masak Cara menghitung nilai absolut dalam SQL.
Tentu saja, jika b = 0
, Anda masih akan mendapatkan kesalahan.
Solusi 3 (benar untuk semua angka):
SELECT a, b, a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2 AS remainder FROM numbers;
Hasilnya adalah:
a | b | sisa |
---|---|---|
9 | 3 | 0 |
5 | 3 | 2 |
2 | 3 | 2 |
0 | 3 | 0 |
-2 | 3 | 1 |
-5 | 3 | 1 |
-9 | 3 | 0 |
5 | -3 | 2 |
-5 | -3 | 1 |
5 | 0 | kesalahan |
0 | 0 | kesalahan |
Diskusi:
Ada cara lain untuk mengatasi masalah ini. Alih-alih CASE WHEN
, gunakan rumus matematika satu baris yang lebih kompleks:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2
Dalam Solusi 2, a % b + ABS(b)
dikembalikan untuk kasus ketika a % b < 0
. Perhatikan bahwa a % b + ABS(b) = a % b + ABS(b) * 1 when a % b < 0
.
Jadi, kita dapat mengalikan ABS(b)
dengan ekspresi yang sama dengan 1 untuk nilai negatif a % b
dan 0
untuk nilai non-negatif a % b
. Sejak a % b
selalu bilangan bulat, ekspresi a % b + 0.5
selalu positif untuk a % b >= 0
dan negatif untuk a % b < 0
. Anda dapat menggunakan bilangan positif apa pun yang kurang dari 1
bukannya 0.5
.
Fungsi tanda SIGN()
mengembalikan 1
jika argumennya benar-benar positif, -1
jika benar-benar negatif, dan 0
jika sama dengan 0
. Namun, Anda memerlukan sesuatu yang hanya mengembalikan 0
dan 1
, bukan 1
dan -1
. Tapi jangan khawatir! Inilah cara Anda memperbaikinya:
(1 - 1) / 2 = 0
(1 - (-1)) / 2 = 1
Kemudian, ekspresi yang benar untuk mengalikan ABS(b)
adalah:
(1 - SIGN(a % b + 0.5)) / 2
Jadi, seluruh rumusnya adalah:
a % b + ABS(b) * (1 - SIGN(a % b + 0.5)) / 2