2.2 Lebegőpont és stabil elemi számítás

2.2.1 Cél, előismeret és induló kérdés

Ennek az egységnek a célja, hogy a programozó a ndarray, dtype, shape és axis fogalmai után 2.1. Python, NumPy és tenzoralak megértse: a kódban tárolt szám nem ugyanaz az objektum, mint a matematikai valós szám, és a kettő közötti eltérést explicit hibamértékkel kell ellenőrizni. A tanulás végére meg tudja különböztetni a matematikai egyenlőséget a numerikus közelítő egyenlőségtől, indokolni tudja az allclose tűréseit, és stabilan tudja kiszámítani a ln∑iexi\ln\sum_i e^{x_i} mennyiséget nagy, illetve negatív bemenetekre. Előismeret: a Python skalár, az np.ndarray, a dtype, a broadcasting és a redukció alapjai 2.1. Python, NumPy és tenzoralak; az állítástípusok megkülönböztetése 1.2. Szakmailag megalapozott modellezési állítások.

Induló kérdés: miért lehet, hogy a 0.1 + 0.2 == 0.3 kifejezés False értéket ad Pythonban, miközben a három szám tizedestört-alakban pontosan ennyi? És mi lehet a helyes ellenőrzés, ha két, elvileg azonos eredményű számítást akarunk összevetni?

2.2.2 Lebegőpontos ábrázolás és a legfontosabb jelenségek

Definíció 1: float32 és float64 normális számok. A kódban használt float32 és float64 az IEEE 754 bináris lebegőpontos formátumra épülő, véges pontosságú ábrázolás. Egy normális float32 szám

x=(−1)s(1+m12−1+m22−2+⋯+m232−23)⋅2e−127,x=(-1)^s(1+m_1 2^{-1}+m_2 2^{-2}+\cdots+m_{23}2^{-23})\cdot 2^{e-127},

ahol s∈{0,1}s\in\{0,1\} az előjelbit, m1,…,m23m_1,\dots,m_{23} a 23 bites tört rész, ee pedig 8 bites, 1≤e≤2541\le e\le 254 kitevőmező. A kettes számrendszerbeli vezető 11 implicit, ezért összesen 24 bites pontosságról szokás beszélni. A float64 analóg: 1 előjelbit, 11 bites kitevő, 52 bites explicit tört, 53 bites teljes pontosság, kitevőeltolás 1023. A nullához közeli, nem normális számok a szubnormális tartományt alkotják, amelyet itt nem használunk részletesen.

Jelölések és alapértékek. Legyen xx a pontos valós szám, x̃\tilde x a reprezentált érték. A gépi epszilon a legkisebb olyan ε\varepsilon, amelyre 1+ε1+\varepsilon a reprezentációban már különbözik 11-től:

ε32=2−23≈1,1920929⋅10−7,ε64=2−52≈2,2204460⋅10−16.\varepsilon_{32}=2^{-23}\approx 1{,}1920929\cdot 10^{-7}, \qquad \varepsilon_{64}=2^{-52}\approx 2{,}2204460\cdot 10^{-16}.

Az egységnyi kerekítési lépés, az ulp (unit in the last place) normális tartományban

ulp(x)=2⌊log2|x|⌋+1−p,\operatorname{ulp}(x)=2^{\lfloor\log_2|x|\rfloor+1-p},

ahol p=24p=24 float32 és p=53p=53 float64 esetén. A legnagyobb normális float32 érték (2−2−23)⋅2127≈3,4028235⋅1038(2-2^{-23})\cdot 2^{127}\approx 3{,}4028235\cdot 10^{38}, míg a legnagyobb float64 érték (2−2−52)⋅21023≈1,7976931⋅10308(2-2^{-52})\cdot 2^{1023}\approx 1{,}7976931\cdot 10^{308}. A legkisebb pozitív normális float32 2−126≈1,1754944⋅10−382^{-126}\approx 1{,}1754944\cdot 10^{-38}.

Definíció 2: overflow, underflow, cancellation, NaN.

Példa a kerekítésre. A 0.1 tizedestört nem véges kettes tizedestört, ezért sem float32, sem float64 nem tárolja pontosan. Emiatt a 0.1 + 0.2 == 0.3 kifejezés False lehet: a három tizedestört külön-külön kerekített közelítése, és a kerekítések után a kettes számrendszerbeli összeg nem pontosan a 0.3 kerekített értéke. A jelenség nem hiba a hardverben, hanem a véges ábrázolás következménye.

2.2.3 Abszolút és relatív hiba levezetése egy kis példán

Definíció 3: hibamértékek. Legyen xx a pontos matematikai érték, x̃\tilde x a kódban kapott érték. Az abszolút hiba

Δa=|x−x̃|,\Delta_a=|x-\tilde x|,

a relatív hiba pedig x≠0x\neq 0 esetén

Δr=|x−x̃||x|.\Delta_r=\frac{|x-\tilde x|}{|x|}.

Ha x=0x=0, a relatív hiba nem értelmes; ilyenkor abszolút hibát vagy nullához közeli nevezővel stabilizált mennyiséget kell használni.

Vizsgáljuk meg a float32 összeadás a=224a=2^{24} és b=1b=1 példáján. A pontos matematikai összeg x=224+1=16777217x=2^{24}+1=16\,777\,217. A float32 ábrázolásban 2242^{24} pontosan megvan, mert a 24 bites szignifikandban az egyetlen vezető egyes és utána 23 nulla éppen elfér. A következő ábrázolható érték ezen a tartományon 224+22^{24}+2, tehát

ulp(224)=224−23=2.\operatorname{ulp}(2^{24})=2^{24-23}=2.

A 224+12^{24}+1 pontosan félúton van a két reprezentálható szám között. A leggyakrabban használt kerekítési szabály, a legközelebbi értékre kerekítés döntetlennél a páros alsó bittel rendelkező értéket választja; itt ez a 2242^{24} érték. Ezért a tárolt összeg

x̃=224=16777216.\tilde x=2^{24}=16\,777\,216.

Az abszolút hiba tehát

Δa=|16777217−16777216|=1,\Delta_a=|16\,777\,217-16\,777\,216|=1,

a relatív hiba pedig

Δr=116777217≈5,9604645⋅10−8.\Delta_r=\frac{1}{16\,777\,217}\approx 5{,}9604645\cdot 10^{-8}.

Ezt kétféleképpen is értelmezhetjük. Egyrészt a hiba 0,50{,}5 ulp, mert az ulp most 22, és 1=0,5⋅21=0{,}5\cdot2. Másrészt a relatív hiba 0,5⋅2−23=2−240{,}5\cdot2^{-23}=2^{-24}, ami a float32 gépi epszilon felével, körülbelül 5,96⋅10−85{,}96\cdot10^{-8}-nel egyenlő. A levezetésből látszik a két hibamérték eltérő szerepe: az abszolút hiba értékfüggő, nagyobb számnál nagyobb lehet, a normális tartományban a szabályos kerekítés relatív hibája viszont korlátos; a példában a korlát 2−242^{-24}.

Korlát mint alkalmazási szabály, nem általános tétel. A fenti megfigyelés a normális tartományra és a legközelebbi értékre kerekítésre vonatkozik. Szubnormális számokra és túlcsorduló eredményekre nem ad ilyen relatív garanciát. A gyakorlatban ezért nem a gépi epszilon önmagában, hanem az adott számítás skálájához igazított atol és rtol a helyes ellenőrzési eszköz.

2.2.4 Stabil exponenciális összegzés: logsumexp előkép

A többosztályos modellek későbbi tárgyalásához szükségünk lesz a log-sum-exp műveletre. Legyen z=(z1,…,zK)∈ℝKz=(z_1,\dots,z_K)\in\mathbb R^K egy logitvektor. Ekkor

L(z)=ln(∑i=1Kezi).L(z)=\ln\left(\sum_{i=1}^{K}e^{z_i}\right).

Ha a ziz_i értékek nagyok, például 10001000 körüliek, a naiv np.exp hívás float32-ben túlcsordul. A cél egy olyan átalakítás, amely ugyanazt a matematikai értéket adja, de véges közbülső értékekkel.

Állítás 1: a max-levonásos stabil alak. Legyen K≥1K\ge 1, minden ziz_i véges, és legyen m=maxizim=\max_i z_i. Ekkor

L(z)=m+ln(∑i=1Kezi−m).L(z)=m+\ln\left(\sum_{i=1}^{K}e^{z_i-m}\right).

Levezetés. Az ezie^{z_i} tagok pozitívak, ezért az összeg pozitív. Az exponenciális szorzási azonossága alapján

∑i=1Kezi=∑i=1Kem+(zi−m)=em∑i=1Kezi−m.\sum_{i=1}^{K}e^{z_i}=\sum_{i=1}^{K}e^{m+(z_i-m)}=e^m\sum_{i=1}^{K}e^{z_i-m}.

A természetes logaritmus pozitív számra szigorúan monoton, és a szorzat logaritmusa a tagok logaritmusának összege:

ln(em∑i=1Kezi−m)=ln(em)+ln(∑i=1Kezi−m).\ln\left(e^m\sum_{i=1}^{K}e^{z_i-m}\right)=\ln(e^m)+\ln\left(\sum_{i=1}^{K}e^{z_i-m}\right).

Mivel ln(em)=m\ln(e^m)=m, a keresett azonosságot kapjuk. A levonás után minden zi−m≤0z_i-m\le 0, tehát minden ezi−me^{z_i-m} érték a (0,1](0,1] tartományba esik, az összeg [1,K][1,K]-ba, a logaritmus pedig [0,lnK][0,\ln K]-ba. Ezért az exponenciális túlcsordulás elkerülhető, feltéve, hogy mm maga véges.

Megjegyzés a tartományokról. Ha a legnagyobb logit is inf, a stabil formula nem segít; az inf bemenet vagy korábbi numerikus hiba, vagy olyan logit, amelyet előbb kezelni kell. Ha a logitok között hatalmas a különbség, a kis tagoknál az ezi−me^{z_i-m} alulcsordulhat nullává; ez a végösszeget általában nem rontja a gyakorlatban, mert a legnagyobb tagnak dominálnia kell, de az alulcsordulás tudatos korlát.

2.2.5 Kézi kis számpélda

Legyen K=3K=3 és z=(1000,1001,1002)z=(1000,1001,1002). A pontos matematikai eredmény float64-ben:

e−2=0,1353352832366127,e−1=0,36787944117144233,e0=1,e^{-2}=0{,}1353352832366127,\quad e^{-1}=0{,}36787944117144233,\quad e^{0}=1,

összegük 1,50321472440805521{,}5032147244080552, ennek természetes logaritmusa 0,40760596456163430{,}4076059645616343, így

L=m+0,4076059645616343=1002,4076059645616.L=m+0{,}4076059645616343=1002{,}4076059645616.

A naiv út float32-ben: np.exp(np.float32(1000)) inf, az összeg inf, a logaritmus inf. A stabil út float32-ben m=1002m=1002, a levonás [-2, -1, 0] tartományú, az eredmény körülbelül 1002,40761002{,}4076 lesz. A float32 kerekítés okozta abszolút hiba körülbelül az ulp feléig terjedhet; 10021002 körül az ulp 210−23=2−13≈0,0001222^{10-23}=2^{-13}\approx 0{,}000122, tehát az eltérés jellemzően 10−410^{-4} nagyságrendű. A float64 referencia és a float32 stabil eredmény összevetéséhez ezért az abszolút tűrést az ulp nagyságrendjéhez kell igazítani.

2.2.6 Labor: naiv és stabil exponenciális összegzés

A labor kódja kis CPU-s NumPy-környezetben fut. Nincs véletlen seed, a műveletek determinisztikusak; a dtype explicit float32, az eszköz a CPU. A futtatási parancs a projektkörnyezetben:

python n006_lab.py

Ha a projekt uv-kezelt környezet, akkor a parancs uv run python n006_lab.py, a projektkonfigurációról lásd [D01].

# n006_lab.py — naiv és stabil logsumexp nagy logitokon
import numpy as np

# Bemenet: 1-D float32 logitvektor
z = np.array([1000.0, 1001.0, 1002.0], dtype=np.float32)

def naive_logsumexp(v):
    # v alakja (K,), float32; itt overflow várható
    return np.log(np.sum(np.exp(v)))

def stable_logsumexp(v):
    # v alakja (K,), float32
    m = np.max(v)                 # max-logit, skalár float32
    shifted = v - m               # (K,) float32, minden elem <= 0
    return m + np.log(np.sum(np.exp(shifted)))

# float32 eredmények
naive = naive_logsumexp(z)
stable = stable_logsumexp(z)

# float64 referencia, a NumPy beépített stabilitását használva
ref64 = np.logaddexp.reduce(z.astype(np.float64))

print("naive:", naive)
print("  finite:", bool(np.isfinite(naive)))
print("stable:", stable)
print("  finite:", bool(np.isfinite(stable)))
print("ref64: ", ref64)

# Ellenőrzés: a stabil float32 érték véges legyen, és
# a float64 referenciához képest a skálához illő tűréssel egyezzen.
assert np.isfinite(stable), "A stabil eredmény nem véges."
assert np.allclose(stable, ref64, rtol=1e-6, atol=2e-4), (
    "A stabil float32 eredmény nem esik a megadott tűrésen belülre."
)

# Negatív nagy logitokon is véges kontroll
z2 = np.array([-1000.0, -1001.0, -1002.0], dtype=np.float32)
print("stable negatív:", stable_logsumexp(z2))
print("finite negatív:", bool(np.isfinite(stable_logsumexp(z2))))

A kimenet az alábbi számított szemléltetés; a konkrét utolsó jegyek a NumPy-verziótól és a lebegőpontos kerekítéstől függhetnek, ezért a kézirat ezt nem állítja mért futásnak:

naive: inf
  finite: False
stable: 1002.4076
  finite: True
ref64:  1002.4076059645616
stable negatív: -999.5924
finite negatív: True

Az np.allclose ellenőrzés az alábbi egyenlőtlenséget vizsgálja elemenként [D04]:

|a−b|≤atol+rtol⋅|b|.|a-b|\le \mathrm{atol}+\mathrm{rtol}\cdot |b|.

Ebben a mérésben bb a float64 referencia. Mivel bb körülbelül 1002,41002{,}4, a relatív tag 10−6⋅1002,4≈10−310^{-6}\cdot 1002{,}4\approx 10^{-3} lenne önmagában; az atol=2e-4 szigorúbb, és a float32 várt ulp-fél-skáláját, 2−14≈6,1⋅10−52^{-14}\approx 6{,}1\cdot10^{-5}-öt, kényelmesen lefedi. A negatív bemenetnél a max-levonás miatt ugyancsak nincs túlcsordulás, és az eredmény véges marad.

A labor értelmezése: a naiv alak inf-et ad, tehát a matematikai érték elveszett; a stabil alak véges, és a magasabb pontosságú referenciával való egyezése a skálához illő abszolút és relatív hibahatáron belül van. Ez a numerikus egyezés implementációs ellenőrzés, nem a matematikai azonosság bizonyítéka; az azonosságot az Állítás 1 levezetése bizonyítja.

2.2.7 Feladatok

F1 (fogalmi, nehézség 1). Magyarázza el, miért lehet egy matematikailag igaz egyenlőség a kódban hamis. Térjen ki a tárolt érték, a kerekítés és az allclose szerepére. Adjon egy saját kis példát is.

F2 (levezetési, nehézség 2). A float32 ábrázolásban 225=335544322^{25}=33\,554\,432, az ulp ezen a tartományon 225−23=42^{25-23}=4. Mi lesz a tárolt érték, ha a float32 reprezentációban kiszámítjuk 225+12^{25}+1-et? Vezesse le az abszolút és relatív hibát.

F3 (implementációs, nehézség 2). Írjon stable_softmax függvényt, amely egy tetszőleges (N, K) alakú float32 logitmátrix minden sorára kiszámítja a stabil softmaxot. A függvény először vonja le a sor maximumát, majd normalizáljon. Ellenőrizze, hogy minden sor összege 11-hez közeli, és a [[1000, 1000], [0, 1]] bemenetre a kimenet [[0.5, 0.5], [1/(1+e), e/(1+e)]] közelítő értéke.

F4 (hibakeresési, nehézség 2). Az alábbi kód float32-ben egy varianciaszámítást végez:

x = np.array([1_000_000.0, 1_000_000.1, 1_000_000.2], dtype=np.float32)
mean_x = np.mean(x)
var_naive = np.mean(x * x) - mean_x * mean_x
print(var_naive)

Mi a matematikai variancia az adott három értékre, és miért kaphat a kód hibás, akár negatív eredményt? Javítsa a számítást központosítással, és adja meg a helyes ellenőrzést float64 referenciával.

F5 (kísérleti/transzfer, nehézség 3). Tervezzen egy kis protokollt annak eldöntésére, hogy egy saját float32 függvény és a float64 referencia implementációja egy adott bemeneti tartományon elfogadhatóan egyezik-e. A protokollban adja meg: a bemeneti vektorok számát és tartományát, a megfigyelési egységet, a kiszámított hibamérőket, a hibahatár indoklását és a döntési szabályt. A feladat nem egy előre megjósolt győztest, hanem egy lefuttatható tervet kér.

2.2.8 Megoldások és indoklás

F1 megoldása. A matematikai egyenlőség a valós számokon értett azonosság. A kódban a számok véges dtype-beli közelítések, a műveletek pedig ezeken a közelítéseken hajtódnak végre, ezért a kerekítési hibák műveletenként halmozódnak. Az 0.1 + 0.2 == 0.3 példában a 0.1, 0.2 és 0.3 egyike sem pontos kettes számrendszerbeli tört, és a közelítő összeg nem pontosan a 0.3 kerekített közelítése. A helyes ellenőrzés nem a pontos egyenlőség, hanem az alkalmazás skálájához illő abszolút és relatív tűréssel végzett összevetés, például np.allclose(a, b, rtol=..., atol=...). Ugyanakkor az allclose használata sem bizonyít matematikai azonosságot; csak azt jelenti, hogy az adott bemeneteken a két implementáció közelítőleg egyezik.

F2 megoldása. A pontos matematikai összeg x=225+1=33554433x=2^{25}+1=33\,554\,433. A [225,226)[2^{25},2^{26}) tartományban az ulp 225−23=42^{25-23}=4, tehát a reprezentálható számok 225,225+4,225+8,…2^{25}, 2^{25}+4, 2^{25}+8,\ldots A 225+12^{25}+1 távolsága 2252^{25}-től 11, a következő reprezentálható értéktől, 225+42^{25}+4-től pedig 33. A legközelebbi értékre kerekítés ezért a 225+42^{25}+4 értéket választja, mert nincs döntetlen:

x̃=225+4=33554436.\tilde x=2^{25}+4=33\,554\,436.

Az abszolút hiba:

Δa=|x−x̃|=|33554433−33554436|=3,\Delta_a=|x-\tilde x|=|33\,554\,433-33\,554\,436|=3,

a relatív hiba pedig

Δr=333554433≈8,9406967⋅10−8.\Delta_r=\frac{3}{33\,554\,433}\approx 8{,}9406967\cdot10^{-8}.

Alakellenőrzés: a hiba 33 kisebb, mint az ulp 44, és a relatív hiba kisebb, mint a float32 gépi epszilon, 1,19⋅10−71{,}19\cdot10^{-7}, összhangban a normális tartomány kerekítési korlátjával. Szélsőeset: ha a bemenet pontosan 2252^{25}, a hiba nulla; ha döntetlen lenne két szomszédos reprezentálható szám között, a páros alsó bit szabálya dönt.

F3 megoldása. A softmax stabil alakja minden ziz_i logitra, m=maxjzjm=\max_j z_j mellett

p̂i=ezi−m∑j=1Kezj−m.\hat p_i=\frac{e^{z_i-m}}{\sum_{j=1}^{K}e^{z_j-m}}.

A függvény:

import numpy as np

def stable_softmax(Z):
    # Z alakja (N, K), float32
    m = np.max(Z, axis=1, keepdims=True)       # (N, 1)
    E = np.exp(Z - m)                          # (N, K)
    return E / np.sum(E, axis=1, keepdims=True) # (N, K)

Z = np.array([[1000.0, 1000.0],
              [0.0, 1.0]], dtype=np.float32)
P = stable_softmax(Z)
print(P)
print("Sorösszegek:", np.sum(P, axis=1))

A kimenet számított ellenőrzés: az első sor [0.5, 0.5], a második sor [0.26894143, 0.7310586] közelítőleg. A sorösszegek [1., 1.]; float32 kerekítéssel a sorösszeg legfeljebb néhány ulp-nyit térhet el 11-től, ezért az ellenőrzés például assert np.allclose(np.sum(P, axis=1), 1.0, atol=1e-6) lehet. A stabilizálás nélkül az első sor inf/inf NaN-t adna, mert e1000e^{1000} float32-ben túlcsordul.

F4 megoldása. A matematikai variancia a három xx értékre: az átlag pontosan 1000000,11\,000\,000{,}1, az eltérések −0,1,0,0,1-0{,}1, 0, 0{,}1, így a variancia 0,01+0+0,013=0,023≈0,0066667\frac{0{,}01+0+0{,}01}{3}=\frac{0{,}02}{3}\approx 0{,}0066667. A float32 x * x értékek 101210^{12} nagyságrendűek; a tárolási hiba ezen a skálán abszolút értelemben már lényegesen nagyobb lehet, mint a keresett 0,00670{,}0067 variancia. A E[x2]−E[x]2\mathrm{E}[x^2]-\mathrm{E}[x]^2 képlet ekkor katasztrofális kivonást hajt végre, és a különbség negatívvá vagy nullává válhat. A javítás a központosított alak:

x = np.array([1_000_000.0, 1_000_000.1, 1_000_000.2], dtype=np.float32)
d = x - np.mean(x)
var_center = np.mean(d * d)
x64 = x.astype(np.float64)
ref = np.mean((x64 - np.mean(x64)) ** 2)
print("javított:", var_center)
print("referencia:", ref)

A javított érték nem negatív, és a float64 referencia körülbelül 0,00666670{,}0066667. Az ellenőrzésnek itt is a várható abszolút hibát kell figyelembe vennie; a központosítás megszünteti a 101210^{12} nagyságrendű köztes értékeket, ezért a hiba a float32 epszilon skálájához közeli lehet.

F5 megoldásmintája. Egy lehetséges protokoll:

2.2.9 Tipikus tévedés ellenpéldával

Az algebrailag ekvivalens képletek nem feltétlenül numerikusan egyenértékűek. Tekintsük a

f1(x)=x+1−x,f2(x)=1x+1+xf_1(x)=\sqrt{x+1}-\sqrt{x},\qquad f_2(x)=\frac{1}{\sqrt{x+1}+\sqrt{x}}

azonosságot. Matematikailag f1(x)=f2(x)f_1(x)=f_2(x) minden x≥0x\ge0-ra, mert a második az első gyöktelenített alakja. Nagy xx-re float32-ben a x+1\sqrt{x+1} és x\sqrt{x} értékek kerekítve egyenlővé válhatnak, így f1f_1 nullát adhat, miközben a pontos érték pozitív. A második alak nem végez közeli értékű tagok kivonását, ezért a pontos értékhez közeli eredményt ad. Az ellenpélda tanulsága: az ekvivalens átalakítás a matematikai objektumon nem jelenti azt, hogy a lebegőpontos implementációk is egyformán stabilak.

2.2.10 Önellenőrzés és korábbi fogalom visszahívása

Az abszolút hiba mérési skálától függ, a relatív hiba a mérési skálát kiküszöböli, de nullánál nem értelmes. A np.allclose két tűrést kombinál, és a b értékkel arányos relatív tűrést használ. Ha egy ellenőrzés inf vagy NaN értéket tartalmaz, az np.allclose alapértelmezésben False-t ad; a np.isfinite és np.isnan először a végesérték-kontroll. A korábbi fogalomhoz visszatérve: a dtype és a shape előzetes rögzítése nélkül a numerikus hiba sem lokalizálható 2.1. Python, NumPy és tenzoralak. A numerikus teszt adott bemenetre vonatkozó empirikus állítás; a matematikai azonosság bizonyítása a levezetés típusú állítás 1.2. Szakmailag megalapozott modellezési állítások.

2.2.11 Rövid összefoglaló

A float32 és float64 véges pontosságú ábrázolás, a hibát abszolút és relatív értékkel mérjük. A 0.1 + 0.2 == 0.3 hamis volta a tizedestört kerekítésének következménye. Az overflow, a katasztrofális kivonás, a NaN és az ulp fogalma megmagyarázza, miért nem helyettesíthető a pontos egyenlőség könyvtári allclose nélkül. A stabil logsumexp a maximum levonásával elkerüli az exponenciális túlcsordulást, és a kis példában véges 1002,40761002{,}4076 értéket ad az inf helyett. A numerikus egyezés implementációs ellenőrzés; a matematikai eredményt levezetés igazolja, a toleranciát pedig az adott skála és dtype kerekítési tulajdonságaihoz kell indokolni.

2.2.12 Következő alkalmazás

A 2.3. Izolált környezet, notebook és verzió egység izolált Python-környezetet, notebook-használatot és verziózást tárgyal. Az ottani verziórögzítés és újrafuttathatóság teszi ellenőrizhetővé az itt bemutatott tolerancia- és végesérték-teszteket; a környezetváltozás ugyanis ugyanolyan hibridet okoz, mint egy megváltozott dtype.

2.2.13 Források