Fura jelenségek BASIC-ben

Elevenítsük fel amit illik tudni a Spectrum 48 BASIC-jéről, BASIC programozásáról...
Avatar
Zozosoft
Speccyalista
Hozzászólások: 726
Csatlakozott: 2012.01.06. 14:03
Kapcsolat:

Re: ZX Spectrum vs. Commodore 64

Hozzászólás Szerző: Zozosoft » 2012.03.22. 13:33

Pgyuri írta: Ki lehet próbálni minden gépen az alábbi programot:

10 FOR i=1 TO 10 STEP 0.1
20 PRINT i
30 IF i=1.3 THEN STOP
40 NEXT i

A futás alapján tessék dönteni.
:shock: :shock: :shock:
Spectrumos történetre mi a magyarázat?
És a Commodore-osra?

EP-n rendben lefut :-)

CPC-n kb ugyanaz mint C64-en. TVC-n jó, de ez elvárható volt a "szocialista EP"-től :-)
A hozzászólást 1 alkalommal szerkesztették, utoljára Zozosoft 2012.03.22. 14:35-kor.

Avatar
makranc
Speccyalista
Hozzászólások: 244
Csatlakozott: 2012.01.11. 11:00
Tartózkodási hely: Budapest, III.

Re: ZX Spectrum vs. Commodore 64

Hozzászólás Szerző: makranc » 2012.03.23. 01:30

Zozosoft írta: Spectrumos történetre mi a magyarázat?
Ha a 20-as számú sorba ezt írod, hogy PRINT i,i-1.3
akkor az 1.3-hoz tartozó értéknek 0-nak kellene lenni, ehelyett
4.6566129E-10.
Ami igen kicsi különbség, de mégsincs egyenlőség.

Nem elég pontos a lebegőpontos?

hpeter
Web Team
Hozzászólások: 37
Csatlakozott: 2012.01.07. 00:25

Re: Fura jelenségek BASIC-ben

Hozzászólás Szerző: hpeter » 2012.03.24. 18:17

Érdekes, hogy jól működik 1, 1.1, 1.2, 1.4 esetében, de hibázik pl. i=3.3 nál is.

Avatar
Asimo
Speccyalista
Hozzászólások: 147
Csatlakozott: 2012.01.09. 18:49

Re: Fura jelenségek BASIC-ben

Hozzászólás Szerző: Asimo » 2012.05.13. 02:36

Egy kicsit hosszan, de levezetném, miért is működik úgy a program Spectrumon, ahogy.

Ha a program így néz csak ki, akkor helyes eredményt kapunk:

Kód: Egész kijelölése

10 LET a=1.3
20 PRINT a,a-1.3
Ha azonban így írjuk a programot:

Kód: Egész kijelölése

10 LET a=1.2
15 LET a=a+0.1
20 PRINT a,a-1.3
Akkor már látható az említett 4.6566129E-10 eltérés, ami egyébként elég kis eltérés.
A fentiekből az is kiderül, hogy nem az 1.3 ábrázolásával van a baj.

De azért nézzük meg, hogyan ábrázolhatók lebegőpontosan (binárisan) ezek a számok:

Kód: Egész kijelölése

1.2 => 1.1999999999...
0.1 => 0.0999999999...
1.3 => 1.2999999999...
Ebből kiderül, hogy ha 1.3-at látunk a képernyőn, akkor az valójában nem is annyi, mi egy kerekített értéket látunk.

Érdekességképpen, itt egy másik program, ami jól mutatja, hogy az eltérés mindig 0 környékén van:

Kód: Egész kijelölése

10 LET a=0
20 FOR i=0 TO 100
30 LET a=a+0.1
40 PRINT a;" ";a-1.3;" ";a-1.4
50 NEXT i
Egy másik példa:

Kód: Egész kijelölése

10 LET b=3.1
20 LET b=b+0.00000001
30 PRINT b
Az eredmény 3.1
Ha eggyel kevesebb 0-t írunk, akkor 3.1000001
Ha pedig b=0 volt, akkor bármilyen kis számot is adunk hozzá, azt kijelzi.

Az eddigiekből látszik, hogy kerekítési kérdésről van szó, nem feltétlenül a Spectrum BASIC hibájáról.

De akkor vegyünk egy konkrét példát: 1.2 + 0.1 - 1.3 <> 0

Kód: Egész kijelölése

 1.2    1.0011001100110011001100110011010
+0.1    0.0001100110011001100110011001101  <- egyforma exponensre hozva, kerekítve
-----------------------------------------
        1.0100110011001100110011001100111  <- az előző kerekítésből már látszik is az alsó biten az eltérés
-1.3    1.0100110011001100110011001100110
-----------------------------------------
        0.0000000000000000000000000000001  <- itt manifesztálódott az eltérés

        1/2^-31 = 0.0000000004656612873... = 4.6566129E-10
Tehát az eltérést 0.1 hozzáadása okozta, mikor egyforma hatványra hozta az összeadás művelet elvégzéséhez.


Pl. ha ugyanezt a műveletet elvégzed Intel X86-os FPU-n, single FP számokkal, akkor az 1.2+0.1-1.3 = 7.15255765726397E-8
Ez azt jelenti, hogy a Spectrum pontosabban, kisebb eltéréssel számolt.
Mondjuk ez logikus is, hiszen Speccy-n a mantissza 8 bittel több.
Persze, ha 6 digitre kerekíted, akkor 0-t kapsz eredményül, erre a pici számra.

Ha single helyett double-t használsz, akkor mivel több digiten ábrázoltad, még
pontosabb eredményt kapsz, az eltérés is nagyságrendekkel kisebb: 4.44522890719057E-17
Itt már akár 16 digitre is lehet kerekíteni a 0-hoz.

Lebegőpontos számoknál a fentiek miatt nem szokás a sima egyenlőséget vizsgálni, hanem azt nézik, hogy a két szám különbsége kellően kis számot ad eredményül. Ennek szellemében átírva a programot, már megfelelően működik:

Kód: Egész kijelölése

10 FOR i=1 TO 10 STEP 0.1
20 PRINT i
30 IF ABS (i-1.3)<0.0000001 THEN STOP
40 NEXT i
Ha pedig a kiírás nem tetszik, akkor érdemes saját formázást, kerekítést alkalmazni.
Egy primitív megoldás:

Kód: Egész kijelölése

PRINT INT (a*1000)/1000
Érdekes, hogy jól működik 1, 1.1, 1.2, 1.4 esetében, de hibázik pl. i=3.3 nál is.
Spectaculator és FUSE emulátorokban 3.3-nál nem hibázott, rendben 2-t írt ki.
A fenti eszmefuttatás után gyakorlatilag érthető is, miért nem hibázik, csak 1.3-nál.
Most itt a PRINT i,i-1.3 kiírásra gondoltam. Csak akkor van "gond", ha egynél kisebb a szám. Ekkor bármilyen kicsi is, megjelenik a nullától eltérő érték.

A háttérben az van, hogy a Speccy max kilenc karakteren írja ki a számokat.
Ha a szám nem fér ki kilenc karakteren, akkor megpróbálja a fent is látható "E" exponens alakban kiírni.
Ha pedig nem fér ki minden digit, akkor kerekít. Az egynél kisebb érték pedig minden esetben kijelezhető, ha máshogy nem, akkor "E" alakban.

Ezért lehet a 3.100000001-ből 3.1
De a 0.0000000004656612873-ből = 4.6566129E-10
Az 1.2999999999999 pedig 1.3

Egy jó példa erre:

Kód: Egész kijelölése

10 LET a=1
20 FOR i=1 TO 32
30 LET a=a/2
40 PRINT a,a+2
50 NEXT i
Ezek után azt kell gondoljam, azokon a gépeken ahol módosítás nélkül jól lefutott a Basic program, valószínűleg az összehasonlító operátor művelet végrehajtása előtt már történt valami implicit kerekítés. Vagy a másik lehetőség, hogy nem pontos a lebegőpontos kalkulátoruk (az baj). A levezetés után úgy gondolom a Spectrum csinálja jól. Visszafejtve és debuggolva az FP összeadást, a Spectrum jó munkát végzett. Az, hogy a kiírásnál és az összehasonlításnál a programozónak kell explicit megadnia, hogy mit szeretne, az is rendben van szerintem.

Avatar
Zozosoft
Speccyalista
Hozzászólások: 726
Csatlakozott: 2012.01.06. 14:03
Kapcsolat:

Re: Fura jelenségek BASIC-ben

Hozzászólás Szerző: Zozosoft » 2012.05.13. 11:53

Enterprise esetén az a magyarázat a helyes működésre, hogy a BASIC számábrázolás BCD-ben történik, 10 BCD karakter + előjeles hatványbájt formában.

Avatar
Asimo
Speccyalista
Hozzászólások: 147
Csatlakozott: 2012.01.09. 18:49

Re: Fura jelenségek BASIC-ben

Hozzászólás Szerző: Asimo » 2012.05.13. 14:57

Zozosoft írta:Enterprise esetén az a magyarázat a helyes működésre, hogy a BASIC számábrázolás BCD-ben történik, 10 BCD karakter + előjeles hatványbájt formában.
És ez nem túl lassú? Pl. itt ez a kis program, ez 61 sec alatt fut le Speccy-n (illetve két emulátoron néztem, Spectaculator és Fuse).

Kód: Egész kijelölése

10 LET n=0
20 LET a=0
30 LET b=1
40 LET b=b/2
50 LET a=a+b
60 LET n=n+1
70 IF b<0.00000001 THEN LET b=1
80 IF a<100 THEN GO TO 40
90 PRINT a,n
Eredmény: 100.5
Ciklusok száma: 2701

Kíváncsi vagyok, Enterprise-on mennyi idő alatt fut le ugyanez a program.
Az EP eredményét megszoroznánk még 1.14329-cel, az összehasonlíthatóság miatt (4MHz vs. 3.5MHz).

Avatar
Zozosoft
Speccyalista
Hozzászólások: 726
Csatlakozott: 2012.01.06. 14:03
Kapcsolat:

Re: Fura jelenségek BASIC-ben

Hozzászólás Szerző: Zozosoft » 2012.05.13. 15:59

Asimo írta:És ez nem túl lassú?
Az IS-BASIC-nek nem a gyorsaság a fő erénye :oops: mondhatni picit túl sokat tud a proci erejéhez képest. (Célszerűbb lett volna az eleve betervezett 6Mhz-es verzióval kijönniük.) Ez egyben válasz Makranc kérdésére, hogy miért is akarok Z180-at faragni az EP-be :D

Eredmény 99s, 100.4999999, 2701

Avatar
Asimo
Speccyalista
Hozzászólások: 147
Csatlakozott: 2012.01.09. 18:49

Re: Fura jelenségek BASIC-ben

Hozzászólás Szerző: Asimo » 2012.05.13. 16:20

Igazából, szerintem itt csak annyit kellett volna, hogy BCD helyett rendes FP ábrázolást használnak. Azzal a műveletek gyorsabbak. De gondolom semmi akadálya egy FP Basic kiegészítést írni IS-hez. Mondjuk manapság egy ekkora meló nem túl kifizetődő. 8-)

Amúgy én is megnéztem ep128emu-val, amivel a következő eredményt kaptam:

Eredmény: 100.4999999
Ciklusok száma: 2701
Futási idő: 123sec @4MHz
Korrigált futási idő: 140sec @3.5MHz

Tehát a Speccy verzió közel 2.3x gyorsabban futott le.
Nem tudom, a különböző eredményt mire véljem. Más emulátor, vagy te igazi HW-n próbáltad?

Megelőzve a kérdést, Speccy-n a végeredmény kijelzése csak a már említett 9digit-re való kerekítés miatt tér el.
Pl. ha még hozzátesszük, hogy LET a=a-100, akkor az eredmény: 0.49999976

Avatar
Zozosoft
Speccyalista
Hozzászólások: 726
Csatlakozott: 2012.01.06. 14:03
Kapcsolat:

Re: Fura jelenségek BASIC-ben

Hozzászólás Szerző: Zozosoft » 2012.05.13. 17:35

Asimo írta: Nem tudom, a különböző eredményt mire véljem.
Gondolom te nem tiltottad le a memória várakozásokat, alapból minden Z80 utasításhoz 1 órajel várakozás van beállítva.
OUT 191,12-vel kapjuk meg a teljes 4MHz CPU teljesítményt.

Avatar
Asimo
Speccyalista
Hozzászólások: 147
Csatlakozott: 2012.01.09. 18:49

Re: Fura jelenségek BASIC-ben

Hozzászólás Szerző: Asimo » 2012.05.13. 20:23

Zozosoft írta:
Asimo írta: Nem tudom, a különböző eredményt mire véljem.
Gondolom te nem tiltottad le a memória várakozásokat, alapból minden Z80 utasításhoz 1 órajel várakozás van beállítva.
OUT 191,12-vel kapjuk meg a teljes 4MHz CPU teljesítményt.
Most, hogy kikapcsoltam 107sec lett (3.5MHz-re korrigálva 122sec). Így pont 2x-es a különbség.

Amúgy, pontosan miért van ez a várakozás? Annyit észrevettem, hogy a kurzor egy picit magasabb hangon szól, ha kikapcsolom.
Okozhat ez bármilyen működési problémát? Az igaz, hogy EP64-en ez a várakozás nem kikapcsolható?

Válasz küldése

Ki van itt

Jelenlévő fórumozók: nincs regisztrált felhasználó valamint 1 vendég