[VC++] Typecasten naar double 'veilig'

Pagina: 1
Acties:

  • SA007
  • Registratie: Oktober 2002
  • Laatst online: 10-02 22:46

SA007

Moderator Tweaking
Topicstarter
Ik heb hier een (red groot) c++ prog wat ik zelf gemaakt heb.
Hier gebruik ik alleen voor alle formaten unsigned integers (is een bep. fileformaat waarin alles UINT is).

Ik doe alleen de volgende operatie vrij vaak:
C:
1
2
3
UINT test=0, test2=1;
double result;
result (double)test / (double)test2;


Het werkt, maar is dit legaal, omdat een int 32bit is en een double 64bit?
Heb liever geen mogelijkheid op bugs door zoiets.

Edit:
Volgens dit topic \[C++] FF een stomme vraag over type casting moet het geen probleem zijn er wordt het gewoon opgevult met nullen, iig bij byte>int, denk dat het voor int>double hetzelfde is...

[ Voor 20% gewijzigd door SA007 op 29-08-2006 09:56 ]


  • Feyd-Rautha
  • Registratie: November 2001
  • Laatst online: 02-08-2025
Je zou het eens moeten testen door waardes van > 2^16 in je test variabele te steken en deze dan te casten naar een double en kijken wat er gebeurt. Bij het casten naar een double wordt de variabele aangevuld met bits, die de waarde hebben van de sign-bit (met '1' dus als je test variabele > 2^16).

1100 1111 ... 0101 (32 bit) --> 1111 1111 1111 ... 1100 1111 ... 0101 (64 bit).

De waarde in result zou dus wel gans anders kunnen zijn na het casten. Maar ik ben het niet 100% zeker. :)

Aangezien ik hier geen C compiler heb, kan ik het zelf niet testen.
In Java wordt er in ieder geval aangevuld met bits die de waarde hebben van de sign-bit. Maar Java kent wel geen unsigned types...

[ Voor 10% gewijzigd door Feyd-Rautha op 29-08-2006 10:10 ]

I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. Where the fear has gone there will be nothing. Only I will remain.


  • SA007
  • Registratie: Oktober 2002
  • Laatst online: 10-02 22:46

SA007

Moderator Tweaking
Topicstarter
@Feyd-Rautha:
Ik heb het al getest en het doet wat ik verwacht, maar de debug builds en release zijn weer anders, en ik kan het niet zomaar ff op ~100 andere pc's testen met andere processors enzo om te testen wat het daar doet.
Het is een applicatie die ook in het buitenland gebruikt gaat worden, dus wil gewoon graag even weten of het gedrag wat ik zie (dat het werkt) normaal is, of dat het eigenlijk niet zo moet.

Ben nog ff aan het testen geweest:

code:
1
2
3
4
5
6
7
8
9
10
printf("%x\n",5);
printf("%i\n",(double)5);
printf("%i\n",(int)(double)5);
printf("%f\n",(int)(double)5);
printf("%f\n",(double)0xFF);
printf("%x\n",12);
printf("%i\n",(double)12);
printf("%i\n",(int)(double)12);
printf("%f\n",(int)(double)12);
printf("%f\n",(double)0x00);


geeft bij debug en release build:
code:
1
2
3
4
5
6
7
8
9
10
5
0
5
0.000000
255.000000
c
0
12
0.000000
0.000000


Waarom 5 en 12?
5 = 0101
12 = 1010

Hij LIJKT dus inderdaad er gewoon 0'en achter te zetten, ik zoek alleen iemand die zeker weet dat dat altijd zo is, kan namelijk niet op een hoop arch's testen hiero (alleen P4 eik).

[ Voor 45% gewijzigd door SA007 op 29-08-2006 10:44 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Een double heeft een 53 bits mantissa, wat derhalve voldoende is om een 32 bits getal zonder verlies van precisie in op te slaan.

Dit is wat anders dan een float, die slechts een 24 bits mantissa heeft. 232-1 (de maximale waarde van een 32 bits unsigned int) kun je bijvoorbeeld niet in een float opslaan en zal worden afgerond naar 232 (welke op zijn beurt weer niet in de unsigned int past, en je rare resultaten krijgt)

C++:
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <iomanip>

int main()
{
    unsigned i = 4294967295;
    std::cout << i << std::endl;
    float f = i;
    std::cout << std::setprecision(15) << f << std::endl;
    i = f;
    std::cout << i << std::endl;
}


output:
code:
1
2
3
4294967295
4294967296
0

[ Voor 72% gewijzigd door .oisyn op 29-08-2006 12:04 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • SA007
  • Registratie: Oktober 2002
  • Laatst online: 10-02 22:46

SA007

Moderator Tweaking
Topicstarter
@.oisyn, maar als je typecast dan heb je dus 32 bits ruimte die je als 64 bits adresseerd, denk niet dat het aantal bits achter de komma daar iets mee te maken heeft.

  • BCC
  • Registratie: Juli 2000
  • Laatst online: 22:35

BCC

Een double is in C++ een mantissa, en dus werkt het met de pricisie nogal iets anders dan een simpele "vul nullen of enen aan". Google eens op mantissa.

[ Voor 73% gewijzigd door BCC op 29-08-2006 12:07 ]

Na betaling van een licentievergoeding van €1.000 verkrijgen bedrijven het recht om deze post te gebruiken voor het trainen van artificiële intelligentiesystemen.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik denk dat je het idee van typecasten niet helemaal snapt :). Als je typecast wordt het ene type geconverteerd naar het andere type. Een float typecasten naar een int geeft dus gewoon het afgeronde getal van die float.

Dit is anders als je pointers of references gaat typecasten naar resp. pointers of references naar andere types. Dan krijg je idd rare bugs omdat je het geheugen ipv de waarde van de double dan ineens gaat interpreteren als int.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

BCC schreef op dinsdag 29 augustus 2006 @ 12:05:
Is een double in C++ een mantissa? Ik dacht alleen floats.
:?
Zowel een float als een double is geen mantissa, maar ze hebben er wel een. Een IEEE floating point getal (zowel de float (32 bits) als de double (64 bits) zijn dat) hebben een sign bit, exponent bits en mantissa bits, en representeert het getal [+/-]mantissa * 2exponent (of speciale waarden zoals infinite, indefinite en NaN)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • BCC
  • Registratie: Juli 2000
  • Laatst online: 22:35

BCC

.oisyn schreef op dinsdag 29 augustus 2006 @ 12:05:
Dit is anders als je pointers of references gaat typecasten naar resp. pointers of references naar andere types. Dan krijg je idd rare bugs omdat je het geheugen van de double dan ineens gaat interpreteren als int.
Het casten van signed naar unsigned is ook vragen om problemen.
Zowel een float als een double is geen mantissa, maar ze hebben er wel een. Een IEEE floating point getal (zowel de float (32 bits) als de double (64 bits) zijn dat) hebben een sign bit, exponent bits en mantissa bits, en representeert het getal [+/-]mantissa * 2exponent (of speciale waarden zoals infinite, indefinite en NaN)
Je hebt gelijk. Ik bedoelde hetzelfde :) Het is zeker alweer 3 jaar geleden dat ik die dingen met de hand moest uitrekenen.

[ Voor 41% gewijzigd door BCC op 29-08-2006 12:12 ]

Na betaling van een licentievergoeding van €1.000 verkrijgen bedrijven het recht om deze post te gebruiken voor het trainen van artificiële intelligentiesystemen.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Net zoveel problemen als elke andere cast tussen types. Als het niet past en daar niet op voorbereid bent ben je de sjaak.

[ Voor 37% gewijzigd door .oisyn op 29-08-2006 12:11 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • SA007
  • Registratie: Oktober 2002
  • Laatst online: 10-02 22:46

SA007

Moderator Tweaking
Topicstarter
Kort gezegd: Geen probleem van uint naar double dus?
Dat als je long naar short gaat mikken krijg je rare shit idd, dat is vrij duidelijk...

  • DroogKloot
  • Registratie: Februari 2001
  • Niet online

DroogKloot

depenisvanjezus

Wat zegt Oisyn nou tegen je? 32 bits passen in 53, dus...

  • SA007
  • Registratie: Oktober 2002
  • Laatst online: 10-02 22:46

SA007

Moderator Tweaking
Topicstarter
waar het me om ging:
53 - 32 = 21 bits
Wat staat er dan in die 21 bits, als ik het goed begrepen heb alleen 0'en, en is er dus niks aan de hand.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Met een beetje logisch nadenken had je er zelf ook wel achter kunnen komen dat als in die 21 bits random zut stond je een enorm onwerkbare situatie hebt ;)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • SA007
  • Registratie: Oktober 2002
  • Laatst online: 10-02 22:46

SA007

Moderator Tweaking
Topicstarter
Nou.. ik werk in dat programma ontzettend veel met calloc, waar daarna weinig data in gezet wordt, dus de kast dat er een paar bytes van het mem 0 zijn is best groot in die app..

En onder de debug modus wordt zoiezo 000000001010101010 gezet achter elk blok data, weet niet hoe dat zit met release want dan kan ik niet direct de variabele waardes uitlezen.

En als die 21 bits de bits achter de komma zijn, en die zijn random zal het nieteens zoveel opvallen, want dan krijg ik in mijn zoomfunties een relatief lage afwijking (minder dan 0.1%)

Al met al had het dus wel gekunt dat het toeval was dat het tot nu toe gewoon goedging.

"Testen bewijst niet dat je programma niet fout is"
- Mijn OS2 leerkracht

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je luistert niet naar wat ik zeg. Als je een waarde in een double hebt staan, en je overschrijft die met een nieuwe waarde afkomstig uit een int, dan is het natuurlijk van de zotte als 21 van die bits niet overschreven worden omdat een int maar 32 bits is. Je converteert een int naar een double, en dus krijg je een volledige representatie van die int in een double. Die representatie klopt niet als 21 van die bits een willekeurige waarde hebben, en krijg je een onwerkbare situatie omdat een conversie van een int naar een double (of elk ander type naar een groter type) niet de waarde oplevert die jij verwacht - er is dan dus eigenlijk helemaal geen conversie mogelijk.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Nou.. ik werk in dat programma ontzettend veel met calloc, waar daarna weinig data in gezet wordt, dus de kastns dat er een paar bytes van het mem 0 zijn is best groot in die app..
Als je geheugen alloceert en niet gebruikt maakt het toch geen fluit uit wat er in staat? Als je daarna een gecaste int naar float daarin mikkert worden heus wel die 53 bits weggeschreven en niet maar 32 omdat het ooit een int was...
Los daarvan zorg calloc() er bij mijn weten voor dat alle bits 0 zijn:
The calloc() function shall allocate unused space for an array of nelem elements each of whose size in bytes is elsize. The space shall be initialized to all bits 0.
Anders lees dit even als je het nog niet helemaal begrijpt.

[ Voor 7% gewijzigd door RobIII op 29-08-2006 16:49 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
.oisyn schreef op dinsdag 29 augustus 2006 @ 12:05:
Ik denk dat je het idee van typecasten niet helemaal snapt :). Als je typecast wordt het ene type geconverteerd naar het andere type. Een float typecasten naar een int geeft dus gewoon het afgeronde getal van die float.

Dit is anders als je pointers of references gaat typecasten naar resp. pointers of references naar andere types. Dan krijg je idd rare bugs omdat je het geheugen ipv de waarde van de double dan ineens gaat interpreteren als int.
Hier draait het allemaal om. Als je een waarde cast, wordt er daadwerkelijk een operatie uitgevoerd op je getal om hem in een andere representatie te krijgen (in dit geval van een unsigned int naar een double).

Disassembly:
code:
1
2
3
4
5
6
7
8
9
//unsigned int myInt = 12345;
  mov   dword ptr [myInt],3039h 
//double myDouble = myInt;
  mov   eax,dword ptr [myInt] 
  mov   dword ptr [ebp-0F4h],eax 
  mov   dword ptr [ebp-0F0h],0 
  fild  qword ptr [ebp-0F4h]  //Laad integer myInt op de Floating-point stack
  fstp  qword ptr [myDouble]  //Pop bovenste element van de Floating-point stack 
                              //als double, en ken het resultaat toe aan myDouble.

Zoals je kan zien zorgt de FILD / FSTP combinatie voor de conversie.

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

om de rest van MrBucket's code te "verklaren":

jouw 32bits van de mantissa wordt in ebp geladen : mov dword ptr [ebp-0F4h],eax
de exponent en rest van de mantissa worden in ebp geladen(wordt effectied op 0 gezet) : mov dword ptr [ebp-0F0h],0
en dan vindt de conversie plaats

offtopic:
staat daar geen mov te veel?
die myInt kan toch meteen in ebp geladen worden, of niet ?

[ Voor 15% gewijzigd door H!GHGuY op 29-08-2006 18:44 ]

ASSUME makes an ASS out of U and ME


  • SA007
  • Registratie: Oktober 2002
  • Laatst online: 10-02 22:46

SA007

Moderator Tweaking
Topicstarter
@mrBuchet:
Dit geld hetzelfde als je typecast naar een double? dan zit het dus goed.

@RobIII:
Ik gebruik het wel helemaal, maar kan goed zijn dat van de 4MB die ik alloceer er maar 10 bytes niet 0 zijn, bitmap data met VEEL blanco data...

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
HIGHGuY schreef op dinsdag 29 augustus 2006 @ 18:43:
om de rest van MrBucket's code te "verklaren":

jouw 32bits van de mantissa wordt in ebp geladen : mov dword ptr [ebp-0F4h],eax
de exponent en rest van de mantissa worden in ebp geladen(wordt effectied op 0 gezet) : mov dword ptr [ebp-0F0h],0
en dan vindt de conversie plaats
Niet helemaal correct. De 2 mov's zorgen ervoor dat de unsigned int wordt uitgebreid naar een quadword (8 bytes) - op dat moment is er nog geen sprake van een exponent maar alleen van 'normale' bits (mantissa zo je wilt). Vervolgens wordt dit quadword op de FPU stack gezet als een 80 bits float, dit is het moment waarop de echte conversie van int naar float plaatsvindt. Tijdens het poppen van de FPU stack wordt de 80 bits float nog een keer geconverteerd, ditmaal naar een double van 64 bits terwijl het weer wordt teruggeschreven als lokale variabele.
offtopic:
staat daar geen mov te veel?
die myInt kan toch meteen in ebp geladen worden, of niet ?
ebp bepaalt het stackframe, het gedeelte op de stack wat gebruikt kan worden voor lokale variabelen. Maar inderdaad, je ziet dat hij 8 bytes op de stack gebruikt voor de conversie terwijl 4 volgens mij voldoende zouden moeten zijn... (dus fild dword ptr [ebp-0F4h], alhoewel je 'm dan niet unsigned hebt ***Edit: dit blijkt de reden te zijn, voor een signed int worden wel gewoon 4 bytes gebruikt***)
SA007 schreef op dinsdag 29 augustus 2006 @ 19:28:
Dit geld hetzelfde als je typecast naar een double? dan zit het dus goed.
Dat was misschien niet helemaal duidelijk van mij. Ja, dat klopt. Zoals eerder al door iemand anders was aangegeven past een unsigned int zonder precisieverlies in een double, wat het een widening conversion* maakt. Voor widening conversions zijn geen casts nodig, en dus heeft de code "myDouble = myInt;" precies dezelfde betekenis als "myDouble = (double)myInt;". In beide gevallen wordt hetzelfde stuk assembly gegenereerd wat een 'echte' conversie voor je uitvoert.

Conclusie (eindelijk :P ) : ja, het is veilig.

* geloof ik, pin me er niet op vast

[ Voor 7% gewijzigd door MrBucket op 29-08-2006 20:21 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 10-12-2025
.oisyn schreef op dinsdag 29 augustus 2006 @ 11:57:
Een double heeft een 53 bits mantissa, wat derhalve voldoende is om een 32 bits getal zonder verlies van precisie in op te slaan.
En 't is bovendien maar 31 bits plus sign :)

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 10-12-2025
SA007 schreef op dinsdag 29 augustus 2006 @ 14:54:
Nou.. ik werk in dat programma ontzettend veel met calloc, waar daarna weinig data in gezet wordt, dus de kast dat er een paar bytes van het mem 0 zijn is best groot in die app..
Let op: all bits zero betekent niet dat een double ook 0.0 is! De exponent kan ook biased zijn. Nou gaat het wel goed met IEEE doubles, maar vziw niet op IBM big iron of Cray's.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

MSalters schreef op dinsdag 29 augustus 2006 @ 21:11:
[...]

En 't is bovendien maar 31 bits plus sign :)
Nee, 't was een unsigned int ;)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 13-02 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

MrBucket schreef op dinsdag 29 augustus 2006 @ 20:13:
Voor widening conversions zijn geen casts nodig, en dus heeft de code "myDouble = myInt;" precies dezelfde betekenis als "myDouble = (double)myInt;"
C++ behoeft sowieso geen expliciete casts tussen primitieven (even afgezien van ptrs en refs uiteraard), het zijn meestal de implentaties die warnings geven. Maar iets als char c = 1234.4567 is geldige C++.

[ Voor 8% gewijzigd door .oisyn op 30-08-2006 12:19 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1