2.39. Float’larla çalışma#

Kayan noktalı sayılar sıradan ondalıklar gibi görünür, ancak davranışlarından birkaçı daha önce bunlarla karşılaşmamış okuyucuları şaşırtır – ve bu davranışlardan biri MicroPython’da masaüstü Python’a kıyasla daha belirgindir. Bu sayfa neler bekleneceğini ve sessizce hatalı davranmayan float kodunun nasıl yazılacağını ele alır.

2.39.1. Hassasiyet#

Python’un float tipi bir IEEE 754 ikili kayan noktalı sayıdır. Çoğu MicroPython derlemesinde tek hassasiyettir (32 bit); masaüstü CPython ise çift hassasiyet (64 bit) kullanır. Tek hassasiyet yaklaşık yedi ondalık basamak doğruluk taşır; çift hassasiyet yaklaşık on beş taşır.

>>> 0.1 + 0.2
0.3000000

>>> 1.0 / 3.0
0.3333333

>>> 1e30 * 1e30
inf

Temsil edilebilir aralık her iki uçta da daha dardır: büyüklüğü yaklaşık 3.4e38‘den büyük sayılar inf‘e taşar ve yaklaşık 1.2e-38‘den küçük sayılar sıfıra yuvarlanır.

2.39.2. Float’ları karşılaştırma#

En yaygın tuzak, eşitliği == ile test etmektir:

>>> 0.1 + 0.2 == 0.3
False

Her iki ifade de eşit olmaları gerekiyormuş gibi görünür, ancak 0.1 + 0.2‘nin sonucu, tam olarak 0.3 olmayan en yakın temsil edilebilir değerdir. Bunun yerine bir tolerans kontrolü kullanın – iki float’ın özdeş olup olmadığını değil, yeterince yakın olup olmadığını sorun:

if abs(a - b) < 1e-6:
    # close enough
    ...

Tolerans seçimi değerlerin ölçeğine bağlıdır. Sayılar 1 mertebesinde olduğunda sabit bir 1e-6 iyi çalışır; değerler büyüklük mertebelerine göre değiştiğinde göreceli bir tolerans daha iyidir.

math.isclose() her ikisini de aynı anda ele alır:

from math import isclose

isclose(0.1 + 0.2, 0.3)         # True
isclose(1.0e6 + 1, 1.0e6)       # True (within default tolerance)

İki anahtar kelime argümanı, hangi tür “yakın”lığın geçerli olacağını kontrol eder:

  • rel_tolgöreceli tolerans, varsayılan 1e-9. İki değer, farkları daha büyük olanın bu kesri içindeyse eşleşir. Herhangi bir ölçekte genel karşılaştırmalar için iyidir.

  • abs_tolmutlak tolerans, varsayılan 0. İki değer, farkları bu sabit miktar içindeyse eşleşir.

math.isclose(), toleranslardan herhangi biri sağlanırsa True döndürür. Varsayılanlar sıfır olmayan sayı çiftlerinin çoğu için uygundur; tuzak, değerlerden birinin tam olarak sıfır olabildiği durumdur. Göreceli tolerans kontrolü “fark ≤ rel_tol × en büyük değer” şeklinde sonuçlanır ve en büyük değer sıfır olduğunda kontrol her zaman başarısız olur:

>>> isclose(0.0, 1e-12)
False

Mutlak tolerans kontrolünde böyle bir sorun yoktur – karşılaştırabileceğiniz değerlerden biri sıfır olduğunda bir abs_tol geçirin:

>>> isclose(0.0, 1e-12, abs_tol=1e-9)
True

2.39.3. Birikim kayması#

Float’ların uzun toplamları MicroPython’da CPython’dakinden daha hızlı hassasiyet kaybeder, çünkü her ara sonuç tekrar 32 bit hassasiyete yuvarlanır:

total = 0.0
for _ in range(1000000):
    total += 0.1

print(total)        # noticeably off from 100000.0

Doğruluğun önemli olduğu tekrarlanan toplamalar için iki desen yardımcı olur:

  • Değerler tam sayılara ölçeklendirilebildiğinde bir tam sayıya biriktirin – saniyeler yerine milisaniyelerle veya voltlar yerine milivoltlarla çalışın, ardından sonda bir kez dönüştürün.

  • Daha küçük yığınlar halinde hesaplayın ve yığın sonuçlarını toplayın; böylece her toplama benzer büyüklükteki değerler arasında olur.

Tam sayı tarafının böyle bir sınırı yoktur – MicroPython tam sayıları, tıpkı CPython’unkiler gibi, keyfi hassasiyettedir. Seçim hakkınız olduğunda, hassasiyet kaybının birikeceği her şeyde tam sayı aritmetiğini tercih edin.