Neden Kayan Nokta Numaraları Yanlış.
Neden bazı sayılar kayan noktalı sayı olarak depolanan doğruluğunu kaybediyor?
Örneğin, ondalık sayı 9.2
ifade edebilir tam olarak bir oran iki ondalık tamsayı (92/10
), hem de olabilir hangi ifade tam olarak ikili (0b1011100/0b1010
). Ancak, aynı oran kayan noktalı sayı olarak depolanan asla tam olarak 9.2
eşittir:
32-bit "single precision" float: 9.19999980926513671875
64-bit "double precision" float: 9.199999999999999289457264239899814128875732421875
Ne kadar basit görünen böyle bir sayı "Çok" olarak ifade etmek için . büyük olabilir ^strong>64 bitbellek?
CEVAP
Çoğu programlama dilinde, kayan nokta sayıları çok scientific notation gibi gösterilir: bir üs ve mantis (significand denir). Çok basit bir numara, 9.2
aslında bu kısmı olduğunu söylüyor:
5179139571476070 * 2-49
Üs -49
ve mantis nerede 5179139571476070
. İmkansız temsil edemezbazıondalık sayılar bu şekilde üs ve mantis her ikisi de tamsayı olmalıdır. Diğer bir deyişle, bir bütün olmalı yüzertamsayıbir çarpı2 tamsayı güç.
9.2
sadece 92/10
, ama olabilir10olarak ifade edilemez2neğerntamsayı değerleri için sınırlı.
Veri görmüyorum
İlk birkaç fonksiyonları içinbakın32 - ve 64-bit yapmak bileşenleri 14**. Eğer sadece bakım eğer bu çıkış (Python örnek): hakkında geçiştirmeye
def float_to_bin_parts(number, bits=64):
if bits == 32: # single precision
int_pack = 'I'
float_pack = 'f'
exponent_bits = 8
mantissa_bits = 23
exponent_bias = 127
elif bits == 64: # double precision. all python floats are this
int_pack = 'Q'
float_pack = 'd'
exponent_bits = 11
mantissa_bits = 52
exponent_bias = 1023
else:
raise ValueError, 'bits argument must be 32 or 64'
bin_iter = iter(bin(struct.unpack(int_pack, struct.pack(float_pack, number))[0])[2:].rjust(bits, '0'))
return [''.join(islice(bin_iter, x)) for x in (1, exponent_bits, mantissa_bits)]
Bu işlev arkasında karmaşıklığı bir çok şey var, ve açıklamak için oldukça teğet olur, ama eğer ilgileniyorsanız, bizim için önemli bir kaynak struct modülüdür.
float
Python 64-bit çift duyarlıklı sayı. C gibi diğer diller, C , C#, Java-hassas çift ayrı bir tür genellikle 64 bit olarak uygulanan double
sahiptir.
Bizim örnek ile bu fonksiyonu çağırdığımızda, 9.2
, burada ne olsun var:
>>> float_to_bin_parts(9.2)
['0', '10000000010', '0010011001100110011001100110011001100110011001100110']
Verilerin yorumlanması
Dönüş değeri yarıyordum üç bileşenlerine göreceksiniz. Bu bileşenler şunlardır:
- İşaret
- Üs
- Mantis (Significand, ya da bir Kısmını)
İşaret
İşareti tek bir parça olarak ilk bileşen saklanır. Bunu açıklamak kolay: 0
kaydır pozitif bir sayı olduğu anlamına gelir; 1
negatif olduğu anlamına gelir. 9.2
pozitif olduğu için işareti değerimiz 0
.
Üs
Üs 11 bit olarak orta bileşen saklanır. , 0b10000000010
bizim durumumuzda. Ondalık, 25 ** değerini temsil eder. Bu bileşen bir cilvesi eşit bir sayı çıkarmak gerekir2(# bit) - 1- 1gerçek üs alma; bizim durumumuzda, bu 0b1111111111
(ondalık sayı 1023
) gerçek üstü, 0b00000000011
(ondalık sayı 3) çıkarma anlamına gelir.
Mantis
Mantis 52 bit olarak üçüncü bileşeni saklanır. Ancak, bu bileşen için bir cilvesi var. Bu durumu anlamak için, bilimsel gösterim, bu gibi: bir dizi düşünün
6.0221413x1023
Mantis 6.0221413
olurdu. Bilimsel gösterimde mantis her zaman sıfır olmayan tek bir rakam ile başlayan bir hatırlayın. Aynı tutar için doğru ikili, ikili, sadece iki basamak dışında: 0
1
. İkili mantisher zaman1
ile başlar! Bir şamandıra saklandığında, ikili mantis önünde 1
alanı kazanmak için atlanmış; geri üçüncü elementimiz önünde yere götürmeliyizdoğrumantis:
1.0010011001100110011001100110011001100110011001100110
Bu bitlerin üçüncü bileşeni içinde saklı aslında temsil ettiği için daha basit bir ek içerirkesirlimantis parçası radix point sağında.
Ondalık sayılar ile uğraşırken, biz "" ya 10 yetkileri ile çarparak bölerek. ondalık noktasını taşımak İkili ya da 2'nin katları ile çarparak bölerek aynı şeyi yapabiliriz. Üçüncü elementimiz 52 bit olduğundan, onun tarafından bölüyoruz252sağa 52 yerlere taşımak için:
0.0010011001100110011001100110011001100110011001100110
Ondalık gösterimde, bu 4503599627370496
675539944105574
0.1499999999999999
elde etmek için bölme gibi. (Bu ikili düzende tam olarak ifade edilebilecek bir oran bir örnektir, ama sadece ondalık yaklaşık; daha fazla ayrıntı için, bkz: 675539944105574 / 4503599627370496.)
Kesirli bir sayı halinde üçüncü bileşeni dönüştürdü olduğumuza göre, 1
ekleme doğru mantis verir.
Bileşenleri özetlemek
- İşareti (birinci bileşen): Negatif Pozitif
0
,1
- Üs (orta bileşen): Çıkarma2(# bit) - 1- 1gerçek üs alma
- Mantis (son bileşen): Böl2(# bit)ve Ekle 40* *gerçek mantis almak için
Sayısının hesaplanması
Birlikte üç parça koyarak, bu ikili sayıyı.
1.0010011001100110011001100110011001100110011001100110 x 1011
Sonra ondalık ikili dönüştürebilirsiniz:
1.1499999999999999 x 23(hatalı!)
Ve bir kayan nokta değeri olarak depolanan (9.2
) ile başladık sayısı son temsilini ortaya çarpın:
9.1999999999999993
Bir Kesir olarak temsil eden
9.2
Numarasını inşa ettiğimize göre, Olası basit bir kesire bunu yeniden oluşturmak için:
1.0010011001100110011001100110011001100110011001100110 x 1011
Tam sayı için Shift mantis:
10010011001100110011001100110011001100110011001100110 x 1011-110100
Ondalık dönüştürmek:
5179139571476070 x 23-52
Üs çıkarma:
5179139571476070 x 2-49
Bölüm içine negatif üs açmak:
5179139571476070 / 249
Üs çarpın:
5179139571476070 / 562949953421312
Eşittir:
9.1999999999999993
9.5
>>> float_to_bin_parts(9.5)
['0', '10000000010', '0011000000000000000000000000000000000000000000000000']
Zaten mantis sadece 4 basamak sıfır bir sürü izler görebilirsiniz. Ama diyelim ki adım geçiyor.
İkili bilimsel gösterimde birleştirin:
1.0011 x 1011
Ondalık noktasını Shift:
10011 x 1011-100
Üs çıkarma:
10011 x 10-1
Maskelerin:
X 2 19-1
Bölüm için negatif üs:
/ 2 191
Üs çarpın:
/ 2 19
Eşittir:
9.5
Daha fazla okuma
- The Floating-Point Guide: What Every Programmer Should Know About Floating-Point Arithmetic, or, Why don’t my numbers add up? () floating-point-gui.de
- What Every Computer Scientist Should Know About Floating-Point Arithmetic (Goldberg 1991)
- IEEE Double-precision floating-point format (Wikipedia)
- Floating Point Arithmetic: Issues and Limitations (docs.python.org)
- Floating Point Binary
Neden kayan nokta sayıları sıfır imzal...
Neden kayan nokta sonsuz, NaNs aksine,...
Neden yok't üstel bir artış içine...
Nasıl Javascript bir tam Sayı kayan no...
Nasıl bölüm Python kayan nokta olması ...