Float en DWord beiden 32bits??

240diesel

Golden Member

Goedenavond, ik zit hier op mijn calculator wat te tikken en kan Frits Kieftebelt goed volgen met zijn uitleg over bit, byte ,word, en dword.

2 tot de macht 1, 8, 16, en 32 geeft respectievelijk;

0-2
0-255
0-65535
4.294.967.296, delen door 2 (pos. en neg.);
-2147483648 - +2147843648

Maar dan ......ik lees dat je met dezelfde 32 bits dezelfde getallen-reeks kunt benoemen maar dan met 3 cijfers achter de komma??

Hier snap ik het dus niet meer, een kwartier google intikken en resultaten lezen maakt mij nog niet wijzer,deels omdat ik het niet kan vatten, deels omdat het niet relevant is voor picbasic.

Zou iemand in Jip en Janneke taal een poging willen doen mij te verklaren hoe je met "maar" 32 bits dezelfde reeks met of zonder getallen achter de komma kunt geven??

Puur binair gecodeerd kun je allen hele getallen maken (integers). Met 32 bits tussen 0 en 2^32. Als je nu iets met een komma wilt opslaan moet je een ander formaat afspreken. Stel we zeggen: van de 32 bits betekend de hoogste 3 bytes wat voor de komma staat, en het onderste byte is achter de komma.
Zo kun je van alles afspreken, maar er zijn standaarden ontstaan die in de praktijk gebruikt worden.

"We cannot solve our problems with the same thinking we used when we created them" - Albert Einstein
240diesel

Golden Member

Dankjewel, dat is helder uitgelegd.

Als ik Frits (in zijn zeer gewaardeerde cursus) even mag citeren uit de eerste les;

"Dan bestaat er in PIC-Basic ook nog DWORD, (Double word) die telt van -2147483648 t/m +2147483647, maar neemt dan ook 32 geheugenplaatsen in (32 bits).

float = drijvend, drijvende komma), die neemt ook 32 bits in (-2147483647,999 t/m 2147483646,999)."

Dan snap ik dus niet dat als je een aantal bytes reserveert voor "achter de komma" dat je "voor de komma" nog steeds hetzelfde getal 2147483646 over kunt houden, dat zou ik dan lager verwachten.

Ik hoop dat ik mij een beetje duidelijk heb uitgedrukt.

[Bericht gewijzigd door 240diesel op 27 maart 2019 21:59:14 (14%)]

Mijn voorbeeld is geen float, maar fixed point. De komma staat op een vast afgesproken plek. Als je komma alle kanten op mag schuiven wordt het een float. Dan moet je dus andere afspraken maken over hoe je dat codeert.
Essentie van m'n verhaal, je kunt allerlei afspraken maken over hoe je getallen in 32 bitst opslaat. Je noemt zelf al die negatieve getallen, da's al anders dan alles positief. Float is weer anders. Waar zit je probleem eigenlijk?

"We cannot solve our problems with the same thinking we used when we created them" - Albert Einstein
240diesel

Golden Member

Mijn probleem zit nu vooral nog tussen mijn oren ;-) dingen die ik begrijp onthoudt ik makkelijker.

Het feit dat Frits in bij wijze van spreken in één adem met 32 bits ook nog drie cijfers àchter de komma toewijst zonder dat het invloed heeft op het getal ervóór brengt mij in verwarring.

Op 27 maart 2019 22:07:49 schreef 240diesel:
Het feit dat Frits in bij wijze van spreken in één adem met 32 bits ook nog drie cijfers àchter de komma toewijst zonder dat het invloed heeft op het getal ervóór brengt mij in verwarring.

Voor wat ik ervan begrepen heb: Een float benaderd slechts het echte getal, er zijn dus waarden die je als float niet kunt opslaan, die worden automatisch afgerond naar de dichtsbijzijnde waarde.

Maar ik kan het mis hebben.

Het stukje van Frits klopt niet, je kan geen getal met zoveel decimalen weergeven.

Bij een float gebruik je 3 bytes voor de mantissa (altijd tussen 0,5 en 1 voor een normalised float) en 1 byte voor de exponent.
De nauwkeurigheid is dan 6 decimalen (24bit) maar het bereik van het kleinste naar grootste getal is veel groter.

Een voorbeeld in het decimale stelsel: 0.5678 * 10^5 = 56780.
(0.5678 is de mantissa, 5 de exponent)
En 0.5678 * 10^-3 = 0.0005678.

Het probleem met floats is dat 0.5678 * 10^5 + 0.5678 * 10^-4 = 56780. Je hebt niet genoeg decimalen om 56780.0005678 weer te geven.

In werkelijkheid werkt het binair (met machten van 2, niet van van 10) maar het principe is het hetzelfde.

Hensz

Golden Member

Je kunt van alles opslaan in 32 bits, tekst bijv., of cijfers per 4 bits één.
Als je maar goed in de gaten houdt wát je hóe opslaat.

Don't Panic!

Op 27 maart 2019 22:41:15 schreef Hensz:
Je kunt van alles opslaan in 32 bits, tekst bijv., of cijfers per 4 bits één.
Als je maar goed in de gaten houdt wát je hóe opslaat.

Zeg eens eerlijk... Jij hebt alleen de titel gelezen he..? :P

Bij een float sla je 3 dingen op: het sign bit (positief of negatief getal), een integer (de mantissa heet dat in een float), en een exponent. Samen kun je daarmee hele grote en hele kleine getallen schrijven, maar met beperkte nauwkeurigheid, want je heb nog steeds maar 32 bits (in dit voorbeeld, er bestaan ook floats met andere formaten, 32 en 64 bit zijn het meest gangbaar.

Laten we even een voorbeeld doen in het decimale stelsel. Als je 6 cijfers mag gebruiken, kun je gewoonlijk een getal tot 999999 weergeven. Echter, als je 2 van die cijfers mag gebruiken als exponent, kan dat 9999*10^99 worden (9999E99 in wetenschappelijke notatie). Deze methode heeft wel een prijs. Als je 100000 wilt schrijven, wordt dat 1000*10^2, maar daarmee is de stapgrootte dus 10^2 ofwel 100 geworden. Dat betekend dat het eerstvolgende grotere getal dat je kunt schrijven 100100 oftewel 1001*10^2 is. Naarmate de exponent groter wordt, neemt de nauwkeurigheid dus af.

Vaak is dit geen probleem; de verschuiving van continenten wordt gemeten in centimeters per jaar, en dan is een paar millimeter meer of minder dus wel belangrijk, maar je gaat je snelheid op de snelweg niet weergeven in millimeters per uur.

Echter, dit geeft wel problemen als je een getal herhaaldelijk bij een groot getal probeert op te tellen. In dat voorbeeld zou 100 keer het getal waarmee je begon, 100000, ophogen met 25 een resultaat geven van... 100000, in plaats van de verwachtte 102500, omdat elke keer die 25 die je erbij optelt, weg valt in de afronding, terwijl het getal 1025*10^2 prima opgeslagen kan worden.

Overigens heeft "Frits" het m.i. fout, je kunt 2147483646,999 helemaal niet opslaan in een 32 bit float, dat wordt afgerond naar 2147483648, en het eerstvolgende grotere getal is 2147483904. De stapgrootte is dus 256, en je kunt nog veel grotere getallen opslaan.

Edit: JoWi was sneller, verhaal komt op hetzelfde neer.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken
240diesel

Golden Member

Wat een goede info!
Daar ben ik erg blij mee, het werd al steeds duidelijker naarmate er meer antwoorden kwamen maar met Sparky's uitleg viel het kwartje helemaal op z'n plaats.

Iedereen heel hartelijk bedankt voor de genomen moeite :-)

Groet, Jan

Hensz

Golden Member

Op 27 maart 2019 22:55:03 schreef BVZ:
[...]
Zeg eens eerlijk... Jij hebt alleen de titel gelezen he..? :P

Leuk geprobeerd, maar ik lees praktisch altijd het hele draadje. Nu dus ook. Ik dacht dat ik er maar eens een andere draai aan moest geven, want wat Diesel nou precies niet snapte was ook niet echt helder en dan helpt een een flinke duw wel eens om tóch die wissel om te krijgen en zo de hersenen van TS uit de spagaat te halen.

Don't Panic!

Natuurlijk, maar de uitleg op Wikipedia is niet bijzonder begrijpelijk en duikt gelijk in de technische details en wiskunde, zonder eerst *uit te leggen* hoe het werkt.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken

Op 27 maart 2019 21:53:11 schreef 240diesel:
...
float = drijvend, drijvende komma), die neemt ook 32 bits in (-2147483647,999 t/m 2147483646,999)."
...

Dat is wel grandioos verwarrend ja! Bij float (floating point numbers) zou ik nooit blijven vasthouden aan het concept van getallen zoals bij integers (byte, word, dword) het geval is.
De door IEEE gestandaardiseerde floating point getallen (waarvan ik redelijkerwijs aanneem dat dat ook de floating point getallen zijn die in jouw taal gebruikt worden) zijn in dit wikipedia artikel beschreven zijn als volgt beschreven
±n*10m ±n*2m
waar n is in (0..16777215) en m is in (-126..127).
Let erop dat hoe verder je getal van 0 af is, hoe minder precisie je hebt. Waar bijvoorbeeld 1 en 2 beschreven kunnen worden, is er "geen" verschil tussen 1-triljard en 1-triljard-en-1. Hierdoor kunnen er vreemde situaties ontstaan, bijvoorbeeld dat (√2)2≠2.

Edit: @rew foutje! het is inderdaad een tweemacht, geen tienmacht.

Meep! Meep!

De range van een 32 bit floating point (in het IEEE-754 formaat!) is

-3.40282 x 10^38 tot +3.40282 x 10^38

240diesel

Golden Member

Ik ben blij te lezen dat het dus niet een vraag naar de bekende weg was, als jullie het al verwarrend vinden hoef ik mij niet te schamen.

Soms denk ik wel eens "had ik maar beter opgelet met wiskunde" , maar ja sommige leraren leken er wel op getraind te zijn de stof zo saai en onaantrekkelijk mogelijk te brengen.

Gelukkig zijn de mogelijkheden om je kennis te vergroten met de jaren enorm toegenomen, mede door dit soort fora. Een mens is nooit te oud om te leren.

Hensz

Golden Member

Op 28 maart 2019 09:58:03 schreef roadrunner84: Let erop dat hoe verder je getal van 0 af is, hoe minder precisie je hebt. Waar bijvoorbeeld 1 en 2 beschreven kunnen worden, is er "geen" verschil tussen 1-triljard en 1-triljard-en-1. Hierdoor kunnen er vreemde situaties ontstaan, bijvoorbeeld dat (√2)2≠2.

Als precisie gedefinieerd is als een absolute waarde, dan klopt het. Belangrijker is echter vaak de nauwkeurigheid, die is met zon 32-bit Fp-representatie wel over het hele bereik even groot.
Je voorbeeld (√2)2≠2 komt daaruit voort. De nauwkeurigheid is eindig, ook bij kleine getallen.

Meestal maakt het geen barst uit of je iets tot 10 cijfers achter de komma nauwkeurig kunt weergeven en is 5-7 cijfers ruim voldoende.
Uiteraard bevestigt hier ook de uitzondering de regel, in de ruimtevaart en de natuurkunde bijv. wil nog wel een met veel grotere nauwkeurigheden gerekend (moeten) worden.

Don't Panic!

Op 28 maart 2019 09:58:03 schreef roadrunner84:
De door IEEE gestandaardiseerde floating point getallen (waarvan ik redelijkerwijs aanneem dat dat ook de floating point getallen zijn die in jouw taal gebruikt worden) zijn in dit wikipedia artikel beschreven zijn als volgt beschreven
±n*10m
waar n is in (0..16777215) en m is in (-126..127).

Als dat inderdaad in hetwikipedia artikel staat dan is dat hartstikke fout. Het is namelijk iets als: w = +/- X * 2^Y. Hierbij is er 1 bit die het +/- gedoe bepaalt, 1<= X < 2 in stapjes van 2^-24 en Y is -127...126 (of iets van die orde).

@blurp hieronder: Jep. Had het zelf ook even gechecked. :-) Het "hidden bit" heb ik niet benoemd, maar zit wel in mijn formule doordat ik 1<=X<2 noem.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Op 28 maart 2019 19:20:24 schreef rew:
[...]Als dat inderdaad in hetwikipedia artikel staat dan is dat hartstikke fout. Het is namelijk iets als: w = +/- X * 2^Y. Hierbij is er 1 bit die het +/- gedoe bepaalt, 1<= X < 2 in stapjes van 2^-24 en Y is -127...126 (of iets van die orde).

Wees gerust Rew, het wikipedia artikel heeft het wel goed. Zelfs gedetailleerder dan jouw omschrijving (wiki noemt het hidden bit, en de details rond subnormal numbers wel)

Toch wel knap uitgelegd door SparkyGSX.

Vorig jaar had ik een routine nodig die zeer snel 32 bit floating point waarden omzette naar ASCII tekst op een Windows PC.
Het moest minimaal 10 X sneller zijn dan de "sprintf functie" in een single thread.
Moest dus het IEEE-754 protocol bestuderen.
Het resultaat was +/- 25 maal sneller dan sprintf.

Voor de berekeningen maak ik gebruik van de SIMD SSE2 instructie set en voer de berekeningen uit in parallel en natuurlijk in assembly. ( Masm )
Alle code is voorzien van commentaar. ( per instructie, vooral om zelf de kluts niet kwijt te raken. :))
Helaas wel in het engels omdat ik het eerder gepost heb in het Masm forum.
In kader van dit onderwerp, misschien interessant om te zien hoe ik het heb geprogrammeerd.

Als er interesse voor is kan ik vanmiddag de assembly source code uploaden. ( ben nu niet thuis )

Gaaf! :-)

Nu ben ik geen fan van X86 assembly (te veel toeters en bellen die er aan geklunst zijn) maar het is toch leerzaam om te kijken naar hoe je een factor 25 kan winnen op een f-to-a.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

Hier is de routine, hoop dat het te volgen is, anders vraag me maar om uitleg te geven.

code:

.code

align 4
Real4_2_ASCII proc Real4string:DWORD,floatnumber:REAL4

    mov         ecx,Real4string         ; The string to fill with the floating point value in ASCII format
    mov         eax,floatnumber         ; Load the floating point value
    test        eax,eax
    je          message_PosZero         ; check if it is 0.0
    cmp         eax,080000000h
    je          message_NegZero         ; Check if it is -0.0
    
    ; check floating-point exceptions
    cmp         eax,07F7FFFFFh
    ja          TestExceptions          ; jump to check floating point exceptions

ProcessReal4:    
    mov         byte ptr [ecx],020h     ; Write " " to the string
    test        eax,80000000h           ; Check the sign bit
    jz          No_SignBit
    mov         byte ptr [ecx],02dh     ; write "-" character to the string
    and         floatnumber,7FFFFFFFh   ; Make it an absolute value
    and         eax,7FFFFFFFh           ; Remove the sign bit
No_SignBit:

    ; Fast Log10(x)-1 routine to calculate the number of digits
    shr         eax,23                  ; Get the 8bit exponent
    sub         eax,127                 ; Adjust for the exponent bias
    cvtsi2ss    xmm0,eax                ; Convert int32 to real4
    mulss       xmm0,Log10_2            ; Approximate Log10(x) == Log10(2) * exponent bits ==  0.30102999566398119 * exponent bits
    addss       xmm0,PowersOfTen[37*4]  ; Add one to get the approximated number of digits from the floating point value
    cvtss2si    eax,xmm0                ; Convert real4 to int32
    mov         ecx,eax                 ; Save approximated number of digits
    mov         edx,38+1                ; Highest possible number of digits + 1
    add         eax,edx                 ; Get the Power Of Ten offset for the digits rounding check

    ; Now do the check to get the exact rounded number of digits from the floating point value
    ; We can do this by comparing it to the closest Power Of Ten below the floating point value  
    movss       xmm0,floatnumber
    comiss      xmm0,PowersOfTen[eax*4-4]
    jc          ExactLog10xMin1         ; Is it below the closest Power Of Ten?
    cmp         ecx,edx                 ; It is above, also check the approximated number of digits
    je          ExactLog10xMin1         ; Is it not above the highest possible number of digits skip adjustment
    dec         edx                     ; Adjust the number of digits by subtracting one
ExactLog10xMin1:                        ; Now we are allmost done to get the exact number of digits
                                        ; There is one exception, the lowest Power Of Ten check value is out of range ( 1.0E+39 )
                                        ; See the last added value in the PowersOfTen table, it's used for the out of range check
    sub         edx,ecx                 ; edx holds the offsets for the PowersOfTen table and the scientific notation string table
    mulss       xmm0,PowersOfTen[edx*4] ; Get the calculated Power Of Ten value and multiply it with the floating point value
    comiss      xmm0,PowersOfTen[38*4]  ; Compare to 10.0
    jnc         ExactNumDigits          ; Is it below 10.0?
    inc         edx                     ; It is below, adjust the offset for the scientific notation string
    mulss       xmm0,PowersOfTen[38*4]  ; Adjust decimal position ( it also solves the out of range issue )
ExactNumDigits:                         ; At this point we have the exact number of digits from the floating point value
    mulss       xmm0,PowersOfTen[42*4]  ; Get the 7 significant digits from the range -1.175494E-38 to 3.402823E+38
    cvtss2si    eax,xmm0                ; We want a Natural Number
    cvtsi2ss    xmm0,eax                ; So, remove the digits after the decimal point

    shufps      xmm0,xmm0,0             ; Splat...., make 4 copies from the real4 number
    movaps      xmm1,xmm0               ; Copy to a total of 8 copies
    mulps       xmm0,dividers           ; Produce base10 numbers
    mulps       xmm1,dividers+16        ; Produce base10 numbers
    movaps      xmm2,xmm0               ; Copy them
    movaps      xmm3,xmm1               ; Copy them
    mulps       xmm2,div10              ; Nullify least significant base10 numbers
    mulps       xmm3,div10              ; Nullify least significant base10 numbers
    cvttps2dq   xmm0,xmm0               ; Truncate remaining fractions
    cvttps2dq   xmm1,xmm1               ; Truncate remaining fractions
    cvtdq2ps    xmm0,xmm0               ; Convert back to real4
    cvtdq2ps    xmm1,xmm1               ; Convert back to real4
    cvttps2dq   xmm2,xmm2               ; Truncate remaining fractions
    cvttps2dq   xmm3,xmm3               ; Truncate remaining fractions
    cvtdq2ps    xmm2,xmm2               ; Convert back to real4
    cvtdq2ps    xmm3,xmm3               ; Convert back to real4
    mulps       xmm2,mul10              ; Move them back in the correct base10 position 
    mulps       xmm3,mul10              ; Move them back in the correct base10 position 
    subps       xmm0,xmm2               ; Subtract to get the extracted digits
    subps       xmm1,xmm3               ; Subtract to get the extracted digits
    cvttps2dq   xmm0,xmm0               ; Convert back to int32
    cvttps2dq   xmm1,xmm1               ; Convert back to int32

    shufps      xmm0,xmm0,11100001b     ; Swap the 2 first digits, the first digit is always zero
                                        ; Now we can write the decimal point for free ( no more memory swaps )
                                        ; Using a prepared ASCIIconverter constant

    packssdw    xmm0,xmm1               ; Pack 8 x 32bit to 8 x 16bit ( signed but, we are within the limit )
    packuswb    xmm0,xmm0               ; Pack 8 x 16bit to 16 x 8bit unsigned
    movq        xmm1,ASCIIconverterE    ; Prepared to insert a decimal point, convert to ASCII and terminate string in one go
    paddb       xmm1,xmm0               ; Convert the number to ASCII

    mov         edx,Scientific_sz[edx*4]; Get the 4 byte scientific notation string
    mov         ecx,Real4string

    movq        qword ptr [ecx+1],xmm1  ; Write the 7 significant digits
    mov         [ecx+9],edx             ; Write the scientific notation string
    ret                                 ; Done....

TestExceptions:
    cmp         eax,07F800000h
    je          message_Inf
    cmp         eax,07F800001h
    je          message_SNaN
    cmp         eax,07FBFFFFFh
    je          message_SNaN
    cmp         eax,07FC00000h
    je          message_QNaN
    cmp         eax,07FFFFFFFh
    je          message_QNaN
    cmp         eax,0FFC00001h
    je          message_QnegNaN
    cmp         eax,0FFBFFFFFh
    je          message_SnegNaN
    cmp         eax,0FF800001h
    je          message_SnegNaN
    cmp         eax,0FFC00000h
    je          message_Indeterm
    cmp         eax,0FF800000h
    je          message_NegInf
    cmp         eax,0FFFFFFFFh
    je          message_QnegNaN
    jmp         ProcessReal4            ; No exceptions found, proceed...  
message_QnegNaN:
    movaps  xmm0,oword ptr szQnegNaN
    movaps  oword ptr [ecx],xmm0
    ret
message_SnegNaN:
    movaps  xmm0,oword ptr szSnegNaN
    movaps  oword ptr [ecx],xmm0
    ret
message_Indeterm:
    movaps  xmm0,oword ptr szIndeterm
    movaps  oword ptr [ecx],xmm0
    ret
message_NegInf:
    movaps  xmm0,oword ptr szNegInf
    movaps  oword ptr [ecx],xmm0
    ret
message_Inf:
    movaps  xmm0,oword ptr szInf
    movaps  oword ptr [ecx],xmm0
    ret
message_SNaN:
    movaps  xmm0,oword ptr szSNaN
    movaps  oword ptr [ecx],xmm0
    ret
message_QNaN:
    movaps  xmm0,oword ptr szQNaN
    movaps  oword ptr [ecx],xmm0
    ret
message_PosZero:
    movaps  xmm0,oword ptr szPosZero
    movaps  oword ptr [ecx],xmm0
    ret
message_NegZero:
    movaps  xmm0,oword ptr szNegZero
    movaps  oword ptr [ecx],xmm0
    ret

Real4_2_ASCII endp

Resultaat van mijn computer:

code:


SIMD Real4 to ASCII conversion by Siekmanski 2018.

1000000 calls per Run for the Cycle counter and the Routine timer.

Intel(R) Core(TM) i7-4930K CPU @ 3.40GHz

 Routine timers running now....

Real4_2_ASCII Clock Cycles: 68 RoutineTime: 0.021774835 seconds
sprintf       Clock Cycles: 1910 RoutineTime: 0.563480065 seconds

Real4_2_ASCII routine is 25.877582 times faster than the sprintf function

Result Real4_2_ASCII:  3.402823e+38
Result sprintf      : 3.402823e+038

Press any key to continue...

Hier de complete source codes en de executable:

Update!
Heb de Scientific_sz lookup table aangepast. ( per ongeluk de verkeerde test versie ingevoegd )
01+e, 02+e en 03+e in de onderste 3 regels van de tabel moesten zijn: 01-e, 02-e en 03-e
Source code aangepast in de download link hieronder. ( sorry voor het ongemak )

http://members.home.nl/siekmanski/Real4_2_ASCII.zip

Nu ben ik toch wel heel nieuwsgierig waarom je zo snel floats moet omzetten naar strings?

Want niemand kan zo snel al die strings lezen....