SQLite'ta sayısal hesap için iki katman vardır. Her derlemede bulunan çekirdek: aritmetik işleçler, round(), abs(), sign() ve toplama fonksiyonları. Bunun yanında sqrt(), pow(), ln(), log(), ceil(), floor(), trigonometrik fonksiyonlar ve pi() gibi matematik fonksiyonları ise ayrı bir gruptur: kaynak kodda bulunurlar ama yalnızca -DSQLITE_ENABLE_MATH_FUNCTIONS derleme seçeneğiyle etkinleşirler. Kullandığınız derlemede yoksa no such function: sqrt hatası alırsınız.
Bu yazıda tamsayı ve ondalık aritmetiğinin kurallarını, sıfıra bölme ve taşma davranışını, matematik fonksiyonlarının tam listesini ve SQLite belgesinin "kayan nokta değerleri yaklaşıktır" uyarısını somut örneklerle ele alıyoruz. Son bölümde para tutarlarını güvenle saklamanın yolunu bir fatura örneğiyle gösteriyoruz. SQLite'ın sayıları hangi depolama sınıfında tuttuğu veri tipleri ve type affinity yazısında anlatılıyor.
Tamsayı ve ondalık aritmetik
SQLite tamsayıları 64 bit işaretli ikiye tümleyen biçiminde saklar; bu aralıktaki her tamsayı kesindir. REAL değerler ise IEEE 754 64 bit kayan nokta sayılarıdır. İfade belgesine göre +, -, * ve / işleçleri iki işlenen de tamsayıysa ve taşma olmayacaksa tamsayı aritmetiği yapar; işlenenlerden biri REAL ise veya tamsayı işlem taşacaksa kayan nokta aritmetiğine geçer. Tamsayı bölmesi sıfıra doğru kesilmiş bir tamsayı verir.
SELECT 7 / 2, 7 / 2.0, -7 / 2, 7 % 3, -7 % 3;
-- çıktı: 3|3.5|-3|1|-1
SELECT 7.5 % 2, typeof(7.5 % 2), quote(7 / 0), quote(7 % 0), quote(7.0 / 0);
-- çıktı: 1.0|real|NULL|NULL|NULL7 / 2 sonucunun 3 olması, ortalama veya yüzde hesaplayan sorgularda en sık görülen hatadır. Ondalık sonuç istiyorsanız işlenenlerden birini REAL yapın (7 / 2.0 veya x * 1.0 / y). % işleci iki işleneni de önce INTEGER'a çevirir; bu yüzden 7.5 % 2 1.5 değil 1.0 verir. Veri tipleri belgesine göre sıfıra bölme NULL üretir; hata vermez. Bu davranış sorguyu çökertmediği için rahat görünse de, raporda boş bir hücrenin nedenini aramak zorunda kalabilirsiniz.
Metinden sayıya örtük dönüşüm
Matematik işleçleri işlenenleri sayı gibi yorumlar. Belgeye göre metin bir gerçek sayıya benziyorsa (ondalık noktası veya üssü varsa) REAL'e, değilse INTEGER'a dönüştürülür. Hiç sayıya benzemeyen bir metin 0 olarak ele alınır ve dönüşüm kayıplı olsa bile yapılır. CAST(... AS NUMERIC) ise kesirli kısmı olmayan değerleri tamsayıya çevirir; bu, örtük dönüşümle arasındaki küçük farktır.
SELECT '3' + '4', '3.0' + 1, 'abc' + 1, '12abc' + 1;
-- çıktı: 7|4.0|1|13
SELECT CAST('12.50' AS INTEGER), CAST('12.50' AS NUMERIC), CAST('12.0' AS NUMERIC), CAST(-3.9 AS INTEGER);
-- çıktı: 12|12.5|12|-3round(), abs() ve sign()

round(X, Y) X değerini ondalık noktadan sonra Y basamağa yuvarlar ve sonucu her zaman kayan nokta sayısı olarak döndürür. Y verilmezse veya negatifse belge onu 0 kabul eder; yani round(1234.5678, -2) yüzlere değil tam sayıya yuvarlar. Yarım değerler sıfırdan uzağa yuvarlanır: 2.5 değeri 3.0, -2.5 değeri -3.0 olur. abs() NULL için NULL, sayıya çevrilemeyen metin için 0.0 döndürür. sign() negatif, sıfır ve pozitif için -1, 0, +1 verir; NULL, BLOB veya kayıpsız sayıya çevrilemeyen bir metin için NULL döner.
SELECT round(2.5), round(-2.5), round(3.14159, 2), round(1234.5678, -2), typeof(round(7));
-- çıktı: 3.0|-3.0|3.14|1235.0|real
SELECT abs(-5), abs(-5.5), abs('x'), quote(abs(NULL));
-- çıktı: 5|5.5|0.0|NULL
SELECT sign(-3), sign(0), sign(2.5), quote(sign('abc'));
-- çıktı: -1|0|1|NULL| Fonksiyon | 2.5 | -2.5 | 1.8 | -1.8 | Dönen tip |
|---|---|---|---|---|---|
| round(X) | 3.0 | -3.0 | 2.0 | -2.0 | REAL |
| ceil(X) | 3.0 | -2.0 | 2.0 | -1.0 | Girdi tipiyle aynı |
| floor(X) | 2.0 | -3.0 | 1.0 | -2.0 | Girdi tipiyle aynı |
| trunc(X) | 2.0 | -2.0 | 1.0 | -1.0 | Girdi tipiyle aynı |
| CAST(X AS INTEGER) | 2 | -2 | 1 | -1 | INTEGER |
Tablodaki ceil(), floor() ve trunc() satırları matematik fonksiyonları grubundandır. Örneklerimizde REAL girdi için REAL, tamsayı girdi için tamsayı döndürdüler. Yalnızca kesirli kısmı atmak istiyorsanız her derlemede çalışan CAST(X AS INTEGER) aynı sonucu tamsayı olarak verir.
Taşma: tamsayı sınırları ve sum() ile total() farkı
64 bit tamsayının üst sınırı 9223372036854775807'dir. Toplama veya çarpma bu sınırı aşacaksa işleçler sonucu REAL olarak hesaplar ve hassasiyet kaybedilir. abs() ise en küçük tamsayı olan -9223372036854775808 için pozitif bir karşılık olmadığından tamsayı taşma hatası verir. Toplama fonksiyonları da farklı davranır: toplama belgesine göre tüm girdiler tamsayıysa sum() taşmada "integer overflow" hatası fırlatır, total() ise her zaman REAL döndürür ve hiçbir zaman taşma hatası vermez.
SELECT typeof(9223372036854775807 + 1), printf('%.3e', 9223372036854775807 + 1);
-- çıktı: real|9.223e+18
SELECT abs(-9223372036854775808);
-- hata: integer overflow
CREATE TABLE b(x INTEGER);
INSERT INTO b VALUES (9223372036854775807), (1);
SELECT sum(x) FROM b;
-- hata: integer overflow
SELECT typeof(total(x)), total(x) > 9e18 FROM b;
-- çıktı: real|1İlk sorgudaki sonucu printf() ile biçimlendirdik, çünkü büyük REAL değerlerin metin gösterimi sürüme göre değişir. Hangi fonksiyonu seçeceğiniz bir tasarım kararıdır: taşmayı fark etmek istiyorsanız sum() sizi uyarır, sonucun her koşulda bir sayı olması gerekiyorsa total() kullanılır. Ayrıca satır yoksa sum() NULL, total() 0.0 döndürür; bu farkın ayrıntısı GROUP BY ve aggregate fonksiyonlar yazısında.
Kayan nokta değerleri yaklaşıktır
SQLite'ın kayan nokta belgesi tek bir cümlenin altını üç kez çizer: kayan nokta değerleri yaklaşıktır. Bu SQLite'a özgü bir kısıt değil, IEEE 754 biçiminin matematiksel sonucudur. Belgedeki örneğe göre bir fiyat sütunundaki kuruş değerlerinden yalnızca 0.00, 0.25, 0.50 ve 0.75 tam olarak gösterilebilir; 47.49 gibi bir değer aslında ona çok yakın ama biraz büyük bir sayı olarak saklanır. Bir IEEE 754 sayısı yaklaşık 15,95 anlamlı basamak tutar.
SELECT 0.1 + 0.2 = 0.3, abs((0.1 + 0.2) - 0.3) < 1e-9, round(0.1 + 0.2, 10) = 0.3;
-- çıktı: 0|1|1
SELECT printf('%.17g', 0.1 + 0.2), printf('%!.17g', 0.1 + 0.2);
-- çıktı: 0.3|0.30000000000000004
SELECT printf('%.2f', 2.675), round(2.675, 2), printf('%.2f', 1.005);
-- çıktı: 2.67|2.67|1.00
SELECT 1e308 * 10, -1e308 * 10;
-- çıktı: Inf|-InfSonuçları yorumlayalım. 0.1 + 0.2 = 0.3 yanlış çıkar; iki ondalık değeri eşitlikle karşılaştırmak yerine farkın küçük bir eşikten az olup olmadığına bakın veya her iki tarafı aynı basamağa yuvarlayın. %.17g biçimi 0.3 yazar, çünkü SQLite'ın biçimlendiricisi varsayılan olarak en fazla 16 anlamlı basamak üretir; ! bayrağı bu sınırı 26 basamağa çıkarır ve gerçek değeri gösterir. 2.675 değeri ikili sistemde 2.675'ten biraz küçük saklandığı için hem printf() hem round() 2.67 verir. Sınırları aşan çarpımlar ise sonsuz değerine ulaşır.
Matematik fonksiyonları

Matematik fonksiyonları 3.35.0 sürümünde eklendi ve yalnızca SQLITE_ENABLE_MATH_FUNCTIONS ile derlenen kütüphanelerde vardır. Belgeye göre argümanlar tamsayı, kayan nokta sayısı veya sayıya benzeyen metin olabilir. Argüman NULL, BLOB veya sayıya kolayca çevrilemeyen bir metinse sonuç NULL olur. Negatif sayının karekökü veya 1'den büyük bir değerin arkkosinüsü gibi tanım kümesi hataları da hata mesajı yerine NULL döndürür. Aşağıdaki örnekler standart bir derlemede çalıştırıldı.
SELECT sqrt(16), quote(sqrt(-1)), pow(2, 10), exp(0), ln(1);
-- çıktı: 4.0|NULL|1024.0|1.0|0.0
SELECT log(100), log10(1000), log(2, 8), log2(1024);
-- çıktı: 2.0|3.0|3.0|10.0
SELECT ceil(1.2), ceil(-1.2), floor(1.8), floor(-1.8), trunc(1.8), trunc(-1.8);
-- çıktı: 2.0|-1.0|1.0|-2.0|1.0|-1.0
SELECT typeof(ceil(1.2)), ceil(5), typeof(ceil(5));
-- çıktı: real|5|integer
SELECT mod(7.5, 2), 7.5 % 2, mod(-7, 3), quote(mod(7, 0));
-- çıktı: 1.5|1.0|-1.0|NULL
SELECT round(pi(), 6), round(degrees(pi()), 6), round(sin(pi() / 2), 6), round(atan2(1, 1) * 4, 6);
-- çıktı: 3.141593|180.0|1.0|3.141593
SELECT sqrt('16'), quote(sqrt('dört')), quote(sqrt(NULL));
-- çıktı: 4.0|NULL|NULL- Üs ve kök:
sqrt(X),pow(X, Y)ve eş adıpower(X, Y),exp(X). - Logaritma:
ln(X)doğal logaritma;log(X)velog10(X)10 tabanlı;log(B, X)B tabanlı;log2(X)2 tabanlı. - Yuvarlama:
ceil(X)ve eş adıceiling(X),floor(X),trunc(X). - Kalan:
mod(X, Y),%işlecine benzer ama tamsayı olmayan argümanlarla da çalışır. - Trigonometri:
sin,cos,tan,asin,acos,atan,atan2(Y, X)ve hiperbolik karşılıkları; açılar radyan cinsindendir.degrees()veradians()dönüşüm yapar,pi()pi sayısının en yakın yaklaşığını verir.
Bu fonksiyonlar olmadan derlenmiş bir kütüphanede, örneğin bu yazıyı hazırlarken kullandığımız komut satırı aracında, aynı çağrı şu hatayı verir. Uygulamanız farklı ortamlarda çalışacaksa matematik fonksiyonlarının varlığına güvenmeden önce hedef derlemede bir deneme sorgusu çalıştırın.
SELECT sqrt(16);
-- hata: no such function: sqrtKesin ondalık hesap: komut satırındaki decimal eklentisi
Kesin sonuç gereken durumlar için kayan nokta belgesi decimal eklentisini anlatır. Eklenti sayıları metin olarak, keyfi duyarlıkla işler; decimal_add(), decimal_sub(), decimal_mul() fonksiyonlarını, kesin toplama yapan decimal_sum() toplama fonksiyonunu ve decimal(X) ile bir kayan nokta sayısının tam ondalık açılımını sağlar. Bölme fonksiyonu yoktur, çünkü bölmenin her zaman sonlu bir ondalık sonucu olmaz. Aynı belgedeki ieee754() fonksiyonu da bir sayıyı mantis ve üs çiftine ayırır.
SELECT decimal_add('0.1', '0.2'), decimal_mul('1.23', '2.34');
-- çıktı: 0.3|2.8782
SELECT decimal(47.49);
-- çıktı: 47.49000000000000198951966012828052043914794921875
SELECT ieee754(47.49);
-- çıktı: ieee754(6683623321994527,-47)Belgeye göre bu eklentiler kütüphanenin birleşik kaynak dosyasına dahil değildir ama komut satırı aracında hazır gelir. Yani bu sorgular sqlite3 isteminde çalışır, uygulamanızın kütüphanesinde ise eklentiyi ayrıca derleyip yüklemediğiniz sürece çalışmaz. decimal(47.49) çıktısı, belgede anlatılan "biraz büyük" değerin kendisidir.
Uygulamalı örnek: para tutarlarını kuruş olarak saklamak
Belgenin fiyat örneği açık bir sonuca götürür: para tutarlarını REAL olarak saklamak, toplamlarda ve karşılaştırmalarda küçük farklar üretebilir. Yaygın ve kesin çözüm, tutarı en küçük para biriminde (kuruş) tamsayı olarak saklamaktır. Tamsayı toplama ve çarpma kesindir; yalnızca yüzde gibi kesirli işlemlerde bir kez, bilinçli olarak yuvarlama yaparsınız.
CREATE TABLE fatura_kalem(
id INTEGER PRIMARY KEY,
urun TEXT,
adet INTEGER NOT NULL,
fiyat_k INTEGER NOT NULL -- birim fiyat, kuruş cinsinden
);
INSERT INTO fatura_kalem(urun, adet, fiyat_k) VALUES
('Mouse', 3, 34990), ('Kablo', 7, 1999), ('Kılıf', 1, 4750);
-- KDV %20, kuruşa yuvarlanmış tamsayı olarak
SELECT urun,
adet * fiyat_k AS ara_k,
CAST(round(adet * fiyat_k * 0.20) AS INTEGER) AS kdv_k
FROM fatura_kalem;
-- çıktı:
-- Mouse|104970|20994
-- Kablo|13993|2799
-- Kılıf|4750|950
SELECT format('%d,%02d TL', sum(adet * fiyat_k) / 100, sum(adet * fiyat_k) % 100) FROM fatura_kalem;
-- çıktı: 1237,13 TLAra toplam tamsayı çarpımıdır ve kesindir. KDV hesabında çarpım bir kez REAL olur, round() ile kuruşa yuvarlanır ve CAST ile yeniden tamsayıya döner. Gösterim için tamsayı bölme ve kalan işleci TL ile kuruşu ayırır; %02d kuruşu iki haneye tamamlar. Tamsayı bölmenin sıfıra doğru kesildiğini unutmayın: negatif tutarlar (iade gibi) gösteriyorsanız işareti ayrıca ele alın. Yuvarlama kuralınızı (her kalemde mi, toplamda mı) muhasebe gereksinimlerinize göre belirleyin; SQLite yalnızca verdiğiniz kuralı uygular.
Sık yapılan hatalar

- Tamsayı bölmesini unutmak:
basarili / toplamiki tamsayıysa sonuç 0 veya 1 olur;basarili * 1.0 / toplamyazın. - Sıfıra bölmede hata beklemek: SQLite NULL döndürür; gerekiyorsa
nullif(toplam, 0)ile niyetinizi açık edin ve NULL sonucu raporda ele alın. - Ondalık değerleri eşitlikle karşılaştırmak:
0.1 + 0.2 = 0.3yanlıştır; eşik veya yuvarlama kullanın. - Para tutarını REAL saklamak: kuruş cinsinden INTEGER kullanın.
- Matematik fonksiyonlarını her derlemede var sanmak: derleme seçeneğine bağlıdırlar.
- log() fonksiyonunu doğal logaritma sanmak: SQLite'ta 10 tabanlıdır; doğal logaritma için
ln()kullanın. - Ondalık çıktının metnine güvenmek: 3.53 ile varsayılan basamak sayısı değişti; sabit biçim için
printf()kullanın.
Sık Sorulan Sorular
SQLite'ta kullandığım derlemede matematik fonksiyonları var mı, nasıl anlarım?
En kolay yol SELECT sqrt(4); çalıştırmaktır; fonksiyon yoksa no such function hatası alırsınız. Derleme seçeneklerini listeleyen PRAGMA compile_options komutu da kullanılabilir; pragma komutlarının genel kullanımı PRAGMA komutları yazısında.
round() neden 2.675 değerini 2.68 yapmıyor?
Çünkü 2.675 ikili kayan nokta biçiminde tam gösterilemez ve 2.675'ten biraz küçük bir değer olarak saklanır. Yuvarlama bu saklanan değere uygulanır. Kesin ondalık davranış gerekiyorsa değeri tamsayı (ör. binde bir birim) olarak saklayın veya komut satırında decimal eklentisini kullanın.
Rastgele sayı nasıl üretilir?
random() yaklaşık -9,2 ile +9,2 kentilyon arasında sözde rastgele bir tamsayı döndürür; belgeye göre en küçük 64 bit değeri hiç üretmez, böylece sonucu her zaman abs() fonksiyonuna verebilirsiniz. 0 ile 99 arası için abs(random()) % 100 yazabilirsiniz. randomblob(N) N baytlık rastgele bir BLOB üretir; belge hex(randomblob(16)) ile benzersiz kimlik üretmeyi örnek gösterir.
Büyük sayıların gösterimi neden sürümden sürüme değişiyor?
REAL bir değerin metne çevrilmesi yuvarlama gerektirir ve 3.53.0 ile bu yuvarlamanın varsayılan basamak sayısı değişti. Değer aynıdır, yalnızca yazılışı farklıdır. Uygulamanız sonuçları metin olarak karşılaştırıyorsa bu fark testleri bozabilir; sayıları sayı olarak karşılaştırın.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: SQLite öğrenme rehberi · Veri tipleri ve affinity · GROUP BY ve aggregate · STRICT tablolar · Metin fonksiyonları ve format() · SQLite 3.53 yenilikler