Kayan nokta değerlerindeki sorun, sabit miktarda bitle sonsuz miktarda (sürekli) değer göstermeye çalışmalarıdır. Doğal olarak, oyunda biraz kayıp olmalı ve bazı değerlerle ısırılmış olacaksınız.
Bir bilgisayar 1.275'i kayan nokta değeri olarak depoladığında, bunun 1.275 veya 1.27499999999999993, hatta 1.27500000000000002 olup olmadığını gerçekten hatırlamaz. Bu değerler iki ondalık sayıya yuvarlandıktan sonra farklı sonuçlar vermelidir, ancak bilgisayar için kayan nokta değerleri olarak saklandıktan sonra tam olarak aynı göründüklerinden ve kaybolan verileri geri yüklemenin bir yolu olmadığından, bu değerler farklı sonuçlar vermelidir . Başka hesaplamalar sadece böyle bir kesinsizlik biriktirecektir.
Dolayısıyla, hassasiyet önemliyse, başlangıçtan itibaren kayan nokta değerlerinden kaçınmanız gerekir. En basit seçenekler
- Bir kullanmak fedakar kütüphane
- değerlerin saklanması ve etrafından geçirilmesi için dizeleri kullanma (string işlemleri eşliğinde)
- tamsayılar kullanın (örneğin, gerçek değerin yüzde biri kadarını geçiyor olabilirsiniz, örneğin dolar cinsinden miktar yerine sent cinsinden tutar)
Örneğin, yüzde birlik sayısını saklamak için tamsayılar kullanırken, gerçek değeri bulma işlevi oldukça basittir:
function descale(num, decimals) {
var hasMinus = num < 0;
var numString = Math.abs(num).toString();
var precedingZeroes = '';
for (var i = numString.length; i <= decimals; i++) {
precedingZeroes += '0';
}
numString = precedingZeroes + numString;
return (hasMinus ? '-' : '')
+ numString.substr(0, numString.length-decimals)
+ '.'
+ numString.substr(numString.length-decimals);
}
alert(descale(127, 2));
Dizelerle yuvarlamaya ihtiyacınız olacak, ancak yine de yönetilebilir:
function precise_round(num, decimals) {
var parts = num.split('.');
var hasMinus = parts.length > 0 && parts[0].length > 0 && parts[0].charAt(0) == '-';
var integralPart = parts.length == 0 ? '0' : (hasMinus ? parts[0].substr(1) : parts[0]);
var decimalPart = parts.length > 1 ? parts[1] : '';
if (decimalPart.length > decimals) {
var roundOffNumber = decimalPart.charAt(decimals);
decimalPart = decimalPart.substr(0, decimals);
if ('56789'.indexOf(roundOffNumber) > -1) {
var numbers = integralPart + decimalPart;
var i = numbers.length;
var trailingZeroes = '';
var justOneAndTrailingZeroes = true;
do {
i--;
var roundedNumber = '1234567890'.charAt(parseInt(numbers.charAt(i)));
if (roundedNumber === '0') {
trailingZeroes += '0';
} else {
numbers = numbers.substr(0, i) + roundedNumber + trailingZeroes;
justOneAndTrailingZeroes = false;
break;
}
} while (i > 0);
if (justOneAndTrailingZeroes) {
numbers = '1' + trailingZeroes;
}
integralPart = numbers.substr(0, numbers.length - decimals);
decimalPart = numbers.substr(numbers.length - decimals);
}
} else {
for (var i = decimalPart.length; i < decimals; i++) {
decimalPart += '0';
}
}
return (hasMinus ? '-' : '') + integralPart + (decimals > 0 ? '.' + decimalPart : '');
}
alert(precise_round('1.275', 2));
alert(precise_round('1.27499999999999993', 2));
Bu işlevin en yakına yuvarlandığını , sıfırdan uzağa bağlandığını , IEEE 754 ise en yakın noktaya yuvarlamayı önerirken kayan nokta işlemleri için varsayılan davranış olarak bile . Bu tür değişiklikler okuyucu için bir egzersiz olarak bırakılır :)