double vs. float

 ( apal | 2010. április 12., hétfő - 23:05 )

udvozlet! kerdes: van valami jo trukk arra, hogy c-ben amellett hogy egy adott valtozo-kupacot explicite float-kent deklaralunk, az alap-muveleteket a mindenfele egyeb (kvazi kikapcsolhatatlan) optimalizaciok mellett is float-szinten ve'gezze el? gcc-4.x alatt, mindezt. (tema: numerikus cuccok elokeszitese/elotesztelese gpu arch-ra, igy az alapmuvelet az lenyegeben +, -, *, / es esetleg sqrt()). thx, A.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Ha jól értem, valami ilyesmi kellene?

int a,b,c;

a=1; b=2;
(float)c = (float)a + (float)b

Ez bizony nem lesz jó, mert baloldalon nem szerepelhet típuskonverzió.

tudom, de arra vagyok kíváncsi, hogy ilyesmire gondolt-e?

Ennek a kérdésnek fuss neki még egyszer, mert nem értem. :)

Egy példa biztos segítene...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

Egy pelda: legyen a,b,c, e's d float, majd ki akarom szamolni a d=a+b*c; erteket. azt szeretnenk, hogy a b*c is float legyen, mint atmeneti ertek. a kod maga, amit tesztelni kene hasonloan egyszeru (+, - es * van csak benne), csak tobb egymasba agyazott ciklusban, fix meretu kicsi tombokben, tobb 100 soron keresztul... es elegge komplex ahhoz hogy belenyulni nem szeretne'k ha nem muszaj.

deviszont: me'g a 387-es idokben is meg lehetett csinalni, sot, gyk ott is az volt az alap, hogy egy ilyen "d=a+b*c" eseten a szorzas eredmenyet osszeadast gepi pontossaggal, es nem a valtozo sajat pontossagaval vegzi (gyk: fld b; fld c; fmulp st(1); fld a; faddp st(1); fstp d; vagy vmi hasonlo). nyilvan egy tobbszaz soros hasonlo jellegu kodot a fordito is, a cpu is ronggya' optimalizal, tehat a fene se tudja hogy mi tortenik (azaz nyilvan egy fstp x; .... fld x; garantalja hogy a masodik visszairasnal x az float pontossagu legyen, de a franc se tudja hogy mire fordul a kod, mert nyilvan nincs mindenhol szukseg erre, ma'r a 387-esben is 8 regiszter volt). egy gpu eseten is persze van optimalizacio de ott garantalt, hogy minden muvelet 32bites maradjon. ezt kellene emulalni valahogy cpu kodon is...

tudnál egy konkrét példát mutatni?
nem vagyok egy nagy c guru (igazából pici sem:) ) és nem tudom elképzelni hol értelmezhet egy floatot mondjuk int ként doubleként

itten van maga a kod, amirol szo lenne. mindke't fv csont nelkul fordul nvcc-vel gpu-ra trivialis valtoztatasok utan (double -> float), es a lenyeg, a szamitasido 99%-a a masodik (_calc) fv-ben van. de ebben csak *, + e's - muveletek vannak. az eom, eln es aux tipusokban levo" tombok ill. matrixok fix meretuek ([8], [8][8], ezt forditasi idoben lehet tudni, es a gpu miatt is csak ilyen lehet, lenyegeben).

Igen, az x87-es utasításnak megvolt az az érdekessége, hogy minden számítás 80 biten történt, és kerekítés csak akkor volt, mikor kivetted a stack-ből az eredményt.
SSE2-nél ilyen már nincs, úgyhogy ha a programodat sse2-vel fordítod, akkor tudtommal biztos lehetsz benne, hogy a float-okon végzett számítás végig float pontossággal történik.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

na ezaz, hogy effele feltetelezesek persze vannak/lehetnek, de ez nagyon arch-fuggo". szoval kerdes, hogy mit kell tenni, hogy relative arch fuggetlenul (azaz mondjuk kvazi modern x86-os es x64-es linuxokon, pl) garantaltan float legyen... a gcc kapcsoloi kozott semmi erre egyertelmuen utalo dolgot nem lattam ;/

Az ilyen SSE-re vonatkozó feltételezéseket nagyon könnyű ellenőrizni. A gcc-vel generáltatsz assembly fájlt, és megnézed a kódot.

-----
Innen most töltsünk tiszta vizet a nyílt kártyákba: ...

Én így tudom. Biztos csak a halál. Egyébként ha ennyire érdekel nézz utána. Vagy ellenőrizd magad. Most már tudod az irányt.

Egyébként Cuda-val lehet CPU-n emulálni a GPU-t, tehát ha csak ez a gond, akkor van megoldás.

Szerk:
Tekintve, hogy SSE kódnál az SSE regisztereket használod, amik 4x32 bitesek float esetben, ezért viszonylag biztos lehetsz benne, hogy már a regiszterekben is a float-ra kerekített érték tárolódik el.

Szerk2:
http://stackoverflow.com/questions/322797/problem-with-floating-point-precision-when-moving-from-i386-to-x86-64

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

Én sem vagyok biztos benne, hogy jól értem a kérdést.
Azt akarod, hogy 32 bites lebegőpontos számokkal történjen minden műveletvégzés?
Akkor nem trükközni kell, hanem ahogy te is írtad, float-ként kell deklarálni a változókat.
Azonban az sqrt()-vel így is gond lesz, mert a deklarációja:
double sqrt(double num);
C++-ban nincs vele gond, mert ott van float sqrt(float num); deklaráció is.
Egy megoldási lehetőség: http://www.dreamincode.net/code/snippet244.htm
Ez elvileg C++-ra van, de csak az i változó deklarációját kell áthelyeni, és már C-ben is jó.

-----
Innen most töltsünk tiszta vizet a nyílt kártyákba: ...

float sqrtf(float x);

(egyébként én se teljesen értem a kérdést:))

Ha jól rémlik, akkor az sqrtf() nem minden platformon (OS-en?) támogatott.

-----
Innen most töltsünk tiszta vizet a nyílt kártyákba: ...

Eh, valóban nem standard, csak ezek a fránya libc-k olyanok, hogy minden csip-csup dolgot beleraknak.

Szerintem szabványos (C99), de ezt nem minden fordító tudja. Bár jelen esetben gcc-ről van szó, és azzal működik.

-----
Innen most töltsünk tiszta vizet a nyílt kártyákba: ...

először a wikipediat néztem, de itt meg c89-nek írja..:

http://www.schweikhardt.net/identifiers.html#isoc

"Egy megoldási lehetőség: http://www.dreamincode.net/code/snippet244.htm"

Na ezt felejtsd el gyorsan... :)
Az első while ciklus teljes marhaság. A többi meg itt van jóval értelmesebben:
http://en.wikipedia.org/wiki/Babylonian_method#Babylonian_method

Amiből kiderül, hogy a második ciklus is túl hosszú, kb 6 iteráció elég double-nál is...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

Az FPU-n nem tudod szimulálni a float számolást, hiszen a FPU regiszterek segítségével vagy 8byte-os, vagy 10 byte-os double-val történik a számolás (hogy melyikkel, az FPU flagektől, fordítóprogramtól, 32/64 bittől függ), és az eredmény valamilyen kerekítés után vissza lesz töltve egy float-ba.

Ha a fordito olyan kodot general(na), hogy minden muvelet utan kiveszi, majd visszateszi az erteket, akkor jo lenne. lasd a fenti pelda, 387-re:

a+b*c => fld a; fld b; fld c; fmulp st(1); faddp st(1); fstp result;

vs.

a+b*c => fld b; fld c; fmulp st(1); fstp tmp; fld a; fld tmp; faddp st(1); fstp result;

nyilvan ezutobbi megoldja a problemat, ha minden fst/fld dword-be (float) megy.

tehat a nkk az, hogy ilyen minden-csak-nem-opt asm kodot generaljon a fordito... vagy barmi ekvivalens. lasd fentebb belinkelt program: fix tombokkel, sok egymasba agyazott ciklus, stbstb, ezt ronggya' lehet optimalizalni ma'r feltucat regiszterrel is. de ~550 sornyi kodot csakugy az ember nem tud atirni...

c++ mennyire jatszik?
float helyett csinalsz sajat "myfloat" osztalyt, atdefinialod az operatorokat, hogy mindenkeppen minden szamitas utan 32 bites legyen, aztan jonapot.. max 20-30 sor, szellosen (utana productionben meg typedef-fel megmondod neki, hogy a myfloat az igazabol kozonseges float, es tessek GPU-ra forditani)

--
I can't believe Steve Jobs's liver is replaceable but the battery in my iPhone is not. - sickipedia