Javascript Matematik İşlem Hataları mı yapiyor?/ Floating-Point Precision Nedir?

Fatih Çalış
Fazla
Published in
4 min readMay 22, 2023
const result = (0.1 + 0.2 === 0.3);
console.log(result);

Üstteki kod için tahmininiz nedir ?

Javascript gibi programlama dillerinde matematik işlemleri genellikle doğru sonuçlar verir. Ancak, bazı durumlarda matematiksel hatalar meydana gelebilir. İşte bazı yaygın nedenler:

  1. Kayan Nokta Hassasiyeti(Floating-Point Precision): Javascript ve diğer birçok programlama dili, kayan nokta aritmetiği kullanarak ondalık sayıları temsil eder. Ancak, kayan nokta aritmetiği bazı kesirli sayıları tam olarak temsil edemez ve hassasiyet sorunlarına yol açabilir. Bu, bazı matematik işlemlerinde yuvarlama hatalarının veya kesirli sayıların tam olarak temsil edilememesinin nedeni olabilir.
  2. Hesaplama Sırası: Matematik işlemlerinin sırası, sonucu etkileyebilir. Özellikle büyük veya karmaşık işlemlerde parantezlerin doğru kullanımına dikkat etmek önemlidir. Yanlış bir sıra, beklenmeyen sonuçlara yol açabilir.
    Örnek:
console.log(2 + 3 * 4); // 14 (Doğru)
console.log((2 + 3) * 4); // 20 (Yanlış)

3.Veri Türü Dönüşümleri: Javascript, otomatik olarak veri türlerini dönüştürme girişiminde bulunur. Bu dönüşümler bazen beklenmeyen sonuçlara neden olabilir. Özellikle sayısal ifadeler ile metin ifadeleri arasında dönüşümler yapılırken dikkatli olunmalıdır.
Örnek:

console.log("2" + 3); // "23" (Metin birleştirme)
console.log("2" - 3); // -1 (Sayısal işlem)jav

Kayan Nokta Aritmetiği (Floating-Point Precision) nedir?

Kayan nokta aritmetiği, bilgisayarlarda ondalık sayıları temsil etmek ve bu sayılar üzerinde matematiksel işlemler yapmak için kullanılan bir yöntemdir. Kayan nokta aritmetiği, IEEE (Electrical and Electronics Engineers) 754 standardına dayanır ve genellikle çoğu modern programlama dili tarafından desteklenir, bunlardan biri de JavaScript’tir.

Kayan nokta aritmetiği, ondalık sayıları iki kısımdan oluşan bir formatta temsil eder: mantis (significand) ve üs (exponent). Mantis, ondalık sayının kesirli kısmını temsil ederken, üs, sayının virgülden önce veya sonra kaç basamak kaydırıldığını belirtir.

IEEE 754 standardı, kayan nokta aritmetiği için iki yaygın formatı tanımlar: Tek hassasiyetli (single precision) ve çift hassasiyetli (double precision). JavaScript varsayılan olarak çift hassasiyetli formatı kullanır, yani 64 bitlik bir sayı formatıdır.

Çift hassasiyetli format, bir kayan nokta sayısını üç bölümde temsil eder:

  1. İşaret Biti (Sign Bit): Bu bit, sayının pozitif veya negatif olmasını belirtir. 0 işaret biti pozitif sayıları, 1 işaret biti ise negatif sayıları temsil eder.
  2. Üstel Kısım (Exponent): Üstel kısım sayının büyüklüğünü ve aralığını belirler. Çift hassasiyetli formatta, 11 bitlik bir alanda depolanır. Bu 11 bit, sayının üstel değerini temsil eder. Ancak, temsil edilen üstel değer doğrudan kullanılmaz. Üstel değer, bir ofset (offset) eklenerek veya çıkarılarak gerçek üstel değere dönüştürülür. Bu ofset işlemi sayesinde üstel kısım hem negatif hem de pozitif üstel değerleri temsil edebilir. Örneğin, 11 bitlik üstel kısmın tamamı 0 olduğunda -1022 üstel değeri, tamamı 1 olduğunda ise +1023 üstel değeri temsil eder.
  3. Mantissa (Fraction): Mantissa, sayının kesirli kısmını temsil eder ve çift hassasiyetli formatta 52 bitlik bir alanda depolanır. Mantissa, kayan noktalı sayının hassas kesirli değerlerini ifade eder. 52 bitlik bu alan, daha yüksek hassasiyet sağlar ve sayının virgülden sonraki rakamlarını daha doğru bir şekilde temsil eder. Mantissa’nın başında 1 bit varsayılan olarak bulunur ve bu bit genellikle gizli bit (hidden bit) olarak adlandırılır. Bu gizli bit, kayan noktalı sayının normalleştirilmiş formunu ifade eder.

Bu şekilde, çift hassasiyetli format 64 bitlik bir veri yapısında işaret, üstel kısım ve matissa olmak üzere üç bölümü birleştirerek ondalık sayıları temsil eder. Bu format, daha geniş bir aralık ve daha yüksek hassasiyet sağlar ve çoğu durumda yeterli doğruluk düzeyini sunar.

Örneğin, JavaScript’te 0.1 veya 3.14 gibi ondalık sayılar çift hassasiyetli format kullanılarak temsil edilir. Ancak, kayan nokta aritmetiğindeki sınırlamalar nedeniyle, bazı ondalık sayılar tam olarak temsil edilemez ve yuvarlama hataları ortaya çıkabilir. Bu nedenle, hassas hesaplamalar yaparken yuvarlama problemlerini dikkate almak önemlidir.

Örnek:

console.log(0.1 + 0.2); // 0.30000000000000004

Sonuç olarak kayan nokta aritmetiği, tam sayılarla kıyaslandığında bazı sınırlamalara sahiptir. Çünkü bilgisayar belleğinde sınırlı alan kullanılarak ondalık sayılar temsil edilir.

Bu tür hassasiyet sorunları, matematiksel işlemlerde dikkatli olmayı gerektirebilir ve özellikle para birimi gibi hassas hesaplamalar yaparken önemlidir.

Yuvarlama problemini çözmek için JavaScript’te genellikle kullanılan yöntemlerden bazıları şunlardır:

  1. toFixed() Metodu: Bu metot, bir sayıyı belirli bir ondalık basamak sayısına yuvarlar ve sonucu bir dize olarak döndürür. Bu yöntem, özellikle finansal uygulamalarda veya sonucun belirli bir hassasiyete sahip olmasını gerektiren durumlarda kullanışlı olabilir.
    Örnek:
const number = 0.1 + 0.2;
console.log(number.toFixed(2)); // "0.30"

2.Math.round() Fonksiyonu: Bu fonksiyon, bir sayıyı en yakın tam sayıya yuvarlar. Eğer yuvarlamak istediğiniz ondalık basamak sayısını belirtmek isterseniz, önce gerekli hassasiyete çarparak yuvarlama işlemini gerçekleştirebilirsiniz.

const number = 0.1 + 0.2;
const roundedNumber = Math.round(number * 100) / 100;
console.log(roundedNumber); // 0.3

3. Math.floor() veya Math.ceil() Fonksiyonları: Bu fonksiyonlar, bir sayıyı aşağı veya yukarı yuvarlar. İhtiyacınıza göre, yukarı veya aşağı yuvarlama işlemini gerçekleştirebilirsiniz.

const number = 0.1 + 0.2;
const roundedNumber = Math.floor(number * 10) / 10;
console.log(roundedNumber); // 0.3

Bu yöntemler, JavaScript’teki yuvarlama problemini çözmek için kullanılan yaygın yaklaşımlardır. Ancak, her durumda kesin bir çözüm sağlamazlar ve sayıların hassasiyetini tamamen korumazlar. Hassas yuvarlama işlemleri gerektiren uygulamalarda, dikkatli bir şekilde yaklaşmak ve doğru yöntemleri kullanmak önemlidir.

Yuvarlama problemini üstel sayıları kullanarak çözebilir miyiz?

JavaScript’te üstel sayıları kullanarak yuvarlama problemini tamamen çözmek mümkün değildir. Fakat üstel sayılar, büyük veya küçük sayıların hassasiyetinin artmasına yardımcı olurlar. Bu sayede yuvarlama hatalarındaki problemleri azaltabilir veya kaçınabiliriz.

Örnek çözüm önerisi:

Number(Math.round(1.005+'e2')+'e-2'); // 1.01

Bu çözümü daha kullanışlı hale getirmek için aşağıdaki fonksiyonu kullanabilirsiniz.

function round(value, decimals) {
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}

round(1.005, 2); // 1.01

Bu tür hataları önlemek için, kayan nokta hassasiyetini dikkate alarak hesaplama yapmaktan kaçınmak, matematik işlemlerini doğru sıra ile yapmak ve doğru veri türü dönüşümlerini yapmak önemlidir. Ayrıca, üçüncü taraf kütüphaneler(Örnek: decimal.js) ve işlemci hassasiyetini artıran yaklaşımlar gibi çözümler de kullanılabilir.

Kaynaklar:

1- https://www.w3schools.com/js/js_numbers.asp

2- IEEE Standard 754 (https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF)

3-https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.htm

4- https://www.codemag.com/article/1811041/JavaScript-Corner-Math-and-the-Pitfalls-of-Floating-Point-Numbers

5- 5-https://floating-point-gui.de/

--

--