[C/C++] Loop over volledige range van datatype

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
Tijdens het schrijven van een vrij simpel programma in C++ liep ik net tegen een voor mij nieuwe situatie aan, die ik het best kan illustreren met een heel kort (niet-representatief) codefragment:
C++:
1
2
3
4
5
6
7
#include <stdint.h>

int main(int argc, char** argv) {
  for (uint8_t index = 0; index <= UINT8_MAX; index++) {
    processUInt8(index);
  }
}


De bovenstaande code is een oneindige loop, omdat 'index' nooit (UINT8_MAX + 1) kan worden en wrapt voordat de for-test 'false' kan geven. Is er een manier om een loop te schrijven die alle waarden van een datatype langsloopt en een index-variabele heeft van het datatype zelf?

Voor zover ik kan nagaan, zou een while of do/while loop dezelfde problemen houden en de enige oplossing die ik kan bedenken is om van 'index' een ander datatype zoals uint16_t of uint32_t te maken.

Omdat ik eigenlijk wil voorkomen dat ik veel heen-en-weer ga casten tussen kleinere en grote datatypes, vroeg ik me af of er een leesbare en simpele oplossing voor dit probleem is? Zoeken op termen als 'C++ loop over full range of data type' levert niet zo snel wat bruikbaars op bij mij.

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • deadinspace
  • Registratie: Juni 2001
  • Laatst online: 10:32

deadinspace

The what goes where now?

raoulduke schreef op maandag 27 augustus 2012 @ 13:36:
Voor zover ik kan nagaan, zou een while of do/while loop dezelfde problemen houden
Dat klopt niet. Een for-loop heeft de conditie-check voor de body, en heeft daarom een iteratiebereik van [0..UINT8_MAX-1]. Een do..while loop heeft de conditie-check na de body, en heeft daarom een iteratiebereik van [1..UINT8_MAX]. Met een do..while loop moet het dus wel degelijk kunnen :)

Acties:
  • 0 Henk 'm!

  • FireDrunk
  • Registratie: November 2002
  • Laatst online: 11:06
Wat is er mis met:

C#:
1
2
3
4
for (int i = 0; i <= (MAX -1); i++)
{
 //Do
}

Even niets...


Acties:
  • 0 Henk 'm!

  • maikoool
  • Registratie: Juli 2009
  • Laatst online: 10-09 18:31
De bovenstaande code is een oneindige loop, omdat 'index' nooit (UINT8_MAX + 1) kan worden en wrapt voordat de for-test 'false' kan geven. Is er een manier om een loop te schrijven die alle waarden van een datatype langsloopt en een index-variabele heeft van het datatype zelf?
C++:
1
2
3
for (uint8_t index = 0; index < UINT8_MAX; index++) { 
    processUInt8(index); 
  }


Lost dat het niet gewoon op? < in plaats van <=. Dan loopt index tot UINT8_MAX.

Acties:
  • 0 Henk 'm!

  • deadinspace
  • Registratie: Juni 2001
  • Laatst online: 10:32

deadinspace

The what goes where now?

FireDrunk schreef op maandag 27 augustus 2012 @ 13:51:
Wat is er mis met:

C#:
1
2
3
4
for (int i = 0; i <= (MAX -1); i++)
{
 //Do
}
Daarmee wordt i = MAX overgeslagen.
maikoool schreef op maandag 27 augustus 2012 @ 13:52:
C++:
1
2
3
for (uint8_t index = 0; index < UINT8_MAX; index++) { 
    processUInt8(index); 
  }


Lost dat het niet gewoon op? < in plaats van <=. Dan loopt index tot UINT8_MAX.
Zelfde verhaal als bovenstaand; daarmee wordt index = UINT8_MAX overgeslagen.

Acties:
  • 0 Henk 'm!

  • FireDrunk
  • Registratie: November 2002
  • Laatst online: 11:06
Dan gebruik je toch < +1?
Maar <= werkt dan toch ook?

C#:
1
2
3
4
for (int i = 0; i <= MAX; i++)
{

}

[ Voor 64% gewijzigd door FireDrunk op 27-08-2012 13:57 ]

Even niets...


Acties:
  • 0 Henk 'm!

  • edeboeck
  • Registratie: Maart 2005
  • Laatst online: 11-09 13:47

edeboeck

mie noow noooothing ...

Is het volgende geen alternatief?
C++:
1
2
3
4
for (int i = -1; i++ <= (MAX -1); )
{
 //Do
}

[ Voor 1% gewijzigd door edeboeck op 27-08-2012 14:02 . Reden: i moet uiteraard in dit geval van -1 vertrekken ]


Acties:
  • 0 Henk 'm!

  • Big Womly
  • Registratie: Oktober 2007
  • Laatst online: 01-09 13:39

Big Womly

Live forever, or die trying

Dan moet jij de TS eens grondig lezen
MAX + 1 geeft een overflow

Kon je in C++ niet zien of de overflow bit was gezet?
Anders zou je ook voor je lus je process kunnen uitvoeren van 0 en vervolgens in je for controleren of die niet terug op 0 staat
Of alles via de lus tot MAX-1 en MAX acteraf nog processen

Maar de do...while{} van deadinspace is nog de properste oplossing levert hetzelfde probleem.
code:
1
2
3
4
5
6
7
8
9
10
index = 0;
do {
  process(index++);
} while (index <= MAX);  //oneindige lus

//of
index=0;
do {
  process(++index);
} while (index <= MAX);  // index == 0 wordt overgeslagen

[ Voor 18% gewijzigd door Big Womly op 27-08-2012 14:06 ]

When you talk to God it's called prayer, but when God talks to you it's called schizophrenia


Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
deadinspace schreef op maandag 27 augustus 2012 @ 13:45:
[...]

Dat klopt niet. Een for-loop heeft de conditie-check voor de body, en heeft daarom een iteratiebereik van [0..UINT8_MAX-1]. Een do..while loop heeft de conditie-check na de body, en heeft daarom een iteratiebereik van [1..UINT8_MAX]. Met een do..while loop moet het dus wel degelijk kunnen :)
Hmm, hoe zie je dit dan voor je, want ik zoek niet [1..UINT8_MAX] maar echt [0..UINT8_MAX]. Bij een do-while of while loop houd je volgens mij altijd dezelfde problemen met de check van 'index' <, <=, ==, > of >= UINT8_MAX zoals in een for-loop?

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • HuHu
  • Registratie: Maart 2005
  • Niet online
C++:
1
2
3
4
5
6
  uint8_t index = -1; // of UINT8_MAX
  do 
  {
    ++index;
    std::cout << (int)index << std::endl; // processUInt8(index);
  } while (index != UINT8_MAX);


Dit geeft een lijst van 0 t/m 255.

[ Voor 5% gewijzigd door HuHu op 27-08-2012 14:06 ]


Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
edeboeck schreef op maandag 27 augustus 2012 @ 13:58:
Is het volgende geen alternatief?
C++:
1
2
3
4
for (int i = -1; i++ <= (MAX -1); )
{
 //Do
}
Ik ben bang dat uint8_t (unsigned) niet correct als -1 geinitialiseerd kan worden.

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 13:19

Reptile209

- gers -

Misschien een beetje een stomme oplossing, maar als je nou loopt van 0 tot < MAX, en vervolgens MAX zelf als losse case na de lus zet?

Zo scherp als een voetbal!


Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
Reptile209 schreef op maandag 27 augustus 2012 @ 14:07:
Misschien een beetje een stomme oplossing, maar als je nou loopt van 0 tot < MAX, en vervolgens MAX zelf als losse case na de lus zet?
Da's inderdaad een goed idee, maar ik heb drie van deze loopjes genest om een 3D-'array' door te lopen; je krijgt dan best lange code om alle gevallen van wel/niet het laatste element door te lopen.

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • HuHu
  • Registratie: Maart 2005
  • Niet online
raoulduke schreef op maandag 27 augustus 2012 @ 14:07:
[...]


Ik ben bang dat uint8_t (unsigned) niet correct als -1 geinitialiseerd kan worden.
Dat kan wel, maar geeft een warning met compileren. Als je hem initialiseert op UINT8_MAX krijg je geen warning.

Acties:
  • 0 Henk 'm!

Verwijderd

Dit is nu leuk. 10 mensen die met dezelfde oplossing aankomen. Je moet niet loopen tot de max. Je moet loopen totdat het getal/datatyle wrapt.

C++:
1
2
3
4
int i = 0; //of whatever
do {
...
} while (i++ != 0);

Edit: of eigenlijk ++i.

Simpel.

(c) Soultaker :P

Uiteraard werkt dat alleen met datatypes die wrappen. Met een float is dat volgens mij niet het geval.

[ Voor 33% gewijzigd door Verwijderd op 27-08-2012 14:14 ]


Acties:
  • 0 Henk 'm!

  • HuHu
  • Registratie: Maart 2005
  • Niet online
Verwijderd schreef op maandag 27 augustus 2012 @ 14:10:
Dit is nu leuk. 10 mensen die met dezelfde oplossing aankomen. Je moet niet loopen tot de max. Je moet loppen totdat het getal/datatyle wrapt.

C++:
1
2
3
4
int i = 0; //of whatever
do {
...
} while (i++ != 0);


Simpel.
Klopt, of je moet beginnen met de wrap, zoals ik deed.

Acties:
  • 0 Henk 'm!

  • edeboeck
  • Registratie: Maart 2005
  • Laatst online: 11-09 13:47

edeboeck

mie noow noooothing ...

raoulduke schreef op maandag 27 augustus 2012 @ 14:07:
[...]Ik ben bang dat uint8_t (unsigned) niet correct als -1 geinitialiseerd kan worden.
Inderdaad, sorry... was even vergeten dat het originele voorbeeld van uint uitging.

Acties:
  • 0 Henk 'm!

  • Reptile209
  • Registratie: Juni 2001
  • Laatst online: 13:19

Reptile209

- gers -

Verwijderd schreef op maandag 27 augustus 2012 @ 14:10:
Dit is nu leuk. 10 mensen die met dezelfde oplossing aankomen. Je moet niet loopen tot de max. Je moet loppen totdat het getal/datatyle wrapt.

C++:
1
2
3
4
int i = 0; //of whatever
do {
...
} while (i++ != 0);


Simpel.
Nice :). Wel *enorm* documenteren, want het leest natuurlijk beroerd, en over een half jaar moet je zelf ook weer even nadenken hoe en waarom je dat ook al weer gedaan had.

Zo scherp als een voetbal!


Acties:
  • 0 Henk 'm!

  • Big Womly
  • Registratie: Oktober 2007
  • Laatst online: 01-09 13:39

Big Womly

Live forever, or die trying

Verwijderd schreef op maandag 27 augustus 2012 @ 14:10:
Dit is nu leuk. 10 mensen die met dezelfde oplossing aankomen. Je moet niet loopen tot de max. Je moet loopen totdat het getal/datatyle wrapt.

C++:
1
2
3
4
int i = 0; //of whatever
do {
...
} while (i++ != 0);


Simpel.

(c) Soultaker :P

Uiteraard werkt dat alleen met datatypes die wrappen. Met een float is dat volgens mij niet het geval.
Dan knalt die er toch bij de eerste controle er al uit?

Je initialiseerd i = 0, controleert of i!=0 (wat false is) en gaat dan pas verhogen (i++)

[ Voor 56% gewijzigd door Big Womly op 27-08-2012 14:43 ]

When you talk to God it's called prayer, but when God talks to you it's called schizophrenia


Acties:
  • 0 Henk 'm!

  • deadinspace
  • Registratie: Juni 2001
  • Laatst online: 10:32

deadinspace

The what goes where now?

Nee, want dan krijg je OF een overflow in UINT8_MAX + 1, OF een overflow in index. C/C++ kennende is UINT8_MAX een int, dus daarin krijg je geen overflow, maar dan overflowt index waardoor je een oneindige loop hebt.
Maar <= werkt dan toch ook?

C#:
1
2
3
4
for (int i = 0; i <= MAX; i++)
{

}
Dat is een oneindige loop, want i <= MAX is altijd waar.
edeboeck schreef op maandag 27 augustus 2012 @ 13:58:
Is het volgende geen alternatief?
C++:
1
2
3
4
for (int i = -1; i++ <= (MAX -1); )
{
 //Do
}
Nee, want je kunt een unsigned integer niet initialiseren op -1. Volgensmij wordt dat onder C/C++ i = MAX, dus de for-loop eindigt meteen.
C++:
1
int i = 0; do { blaat; i++; } while( i != UINT8_MAX );
... want ik zoek niet [1..UINT8_MAX] maar echt [0..UINT8_MAX].
Je begrijpt me verkeerd; een for-loop (met pre-conditiecheck) is te gebruiken voor 0 t/m UINT8_MAX - 1 iteraties. Een do..while loop is te gebruiken voor 1 t/m UINT8_MAX iteraties. Jij wil UINT8_MAX iteraties, dus een for-loop is geen optie, een do..while wel.
Reptile209 schreef op maandag 27 augustus 2012 @ 14:12:
Nice :). Wel *enorm* documenteren, want het leest natuurlijk beroerd, en over een half jaar moet je zelf ook weer even nadenken hoe en waarom je dat ook al weer gedaan had.
Serieus? Ik vind het een vrij standaard idioom.

Acties:
  • 0 Henk 'm!

  • HuHu
  • Registratie: Maart 2005
  • Niet online
Big Womly schreef op maandag 27 augustus 2012 @ 14:13:
[...]


Dan knalt die er toch bij de eerste controler er al uit?
Lees de code eens goed. Het is een do-while en de index wordt verhoogt vóórdat de controle plaatsvindt. Bij de eerste ++index gaat index van 255 naar 0 en dan begint het.

Acties:
  • 0 Henk 'm!

  • Big Womly
  • Registratie: Oktober 2007
  • Laatst online: 01-09 13:39

Big Womly

Live forever, or die trying

HuHu schreef op maandag 27 augustus 2012 @ 14:15:
[...]

Lees de code eens goed. Het is een do-while en de index wordt verhoogt vóórdat de controle plaatsvindt. Bij de eerste ++index gaat index van 255 naar 0 en dan begint het.
Zie mijn edit :P Als je eerst verhoogd (++index) gaat MAX nooit uitgevoerd worden, als je achteraf verhoogd (index++) doorloop je enkel index==0

When you talk to God it's called prayer, but when God talks to you it's called schizophrenia


Acties:
  • 0 Henk 'm!

  • HuHu
  • Registratie: Maart 2005
  • Niet online
Big Womly schreef op maandag 27 augustus 2012 @ 14:18:
[...]


Zie mijn edit :P Als je eerst verhoogd (++index) gaat MAX nooit uitgevoerd worden, als je achteraf verhoogd (index++) doorloop je enkel index==0
Klopt, de code van Darkstone werkt inderdaad niet. Ik dacht dat je daar eerst mijn code had staan, voor je edit? Daar was mijn commentaar voor bedoelt.

Acties:
  • 0 Henk 'm!

  • Big Womly
  • Registratie: Oktober 2007
  • Laatst online: 01-09 13:39

Big Womly

Live forever, or die trying

Ik zag darkstone's code in jouw quote en had verkeerd de verkeerde gequote (hence the :P) ;) :P

Er zijn dus 4 mogelijke oplossingen (gesorteerd naar persoonlijke voorkeur van best naar minst)
- Je itereert van 0 tot MAX-1 en doet MAX achteraf
- Je doet 0 op voorhand en begint te iteren van 1..overflow
- Je zorgt ervoor dat je index verder gan gaan dan MAX
- Je zoekt jezelf een compiler die je toegang geeft tot de overlow flag

Niet zelf gelezen, maar lijkt me nuttig leesvoer voor dit probleem: http://ptgmedia.pearsoncm...echapter/seacord_ch05.pdf
bron: http://stackoverflow.com/...t-integer-overflow-in-c-c

[ Voor 88% gewijzigd door Big Womly op 27-08-2012 14:32 ]

When you talk to God it's called prayer, but when God talks to you it's called schizophrenia


Acties:
  • 0 Henk 'm!

  • Mijzelf
  • Registratie: September 2004
  • Niet online
Verwijderd schreef op maandag 27 augustus 2012 @ 14:10:
Uiteraard werkt dat alleen met datatypes die wrappen. Met een float is dat volgens mij niet het geval.
Een float kun je op deze manier zowiezo niet verwerken. Je kunt niet met stapjes van 1 door het hele bereik lopen, aangezien 1 voor het grootste deel van het bereik onder de resolutie valt.

Acties:
  • 0 Henk 'm!

  • Aloys
  • Registratie: Juni 2005
  • Niet online
Met een do while moet het wel degelijk kunnen hoor

C:
1
2
3
4
int index = 0;
do {
   iets(index);
} while(index < intmax && index++)

Volgens mij kom je dan ook alle situaties tegen.

Volgens mij gaat dit ook goed:
C:
1
2
3
4
int index = 0;
do {
   iets(index);
} while(++index)

Als je nu terug komt bij 0 stopt de lus. :)

[ Voor 28% gewijzigd door Aloys op 27-08-2012 15:10 ]


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Ik snap alleen niet helemaal waarvoor je nu een oplossing aan het zoeken bent. Als je een array alloceert met grootte N items zal deze lopen tot index N-1. Als je 'em dus alloceert met 2^32 zal de hoogste index 2^32 -1 zijn.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
Ik heb zo'n fout ook al eens in het slechte programmeervoorbeelden topic gepost.

Pseudo code, en omdat dit zo simpel is mag je zelf bedenken of dat nu do while, do until, of een mix dit is.
code:
1
2
3
4
5
6
7
8
i = 0
while (1)
{
    do_something(i);
    if (i == MAX)
        leave;
    i++;
}


edit:

Aloys heeft 't natuurlijk ook goed.

[ Voor 55% gewijzigd door Sendy op 27-08-2012 15:02 ]


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 18:51
raoulduke schreef op maandag 27 augustus 2012 @ 14:07:
Ik ben bang dat uint8_t (unsigned) niet correct als -1 geinitialiseerd kan worden.
Belangrijk is om te onthouden dan in C/C++ signed integers niet mogen wrappen, maar unsigned integers wel. Dat geldt ook voor conversies, dus -1 casten naar unsigned gaat gegarandeerd goed.

Aloys postte deze code:
Aloys schreef op maandag 27 augustus 2012 @ 14:51:
Volgens mij gaat dit ook goed:
C:
1
2
3
4
int index = 0;
do {
   iets(index);
} while(++index)

Als je nu terug komt bij 0 stopt de lus. :)
Deze code is fout (en de kans is groot dat een moderne compiler hier een oneindige lus genereert) maar met een unsigned type (waar het in de TS om ging) is 'ie wél correct.

Voor signed integers zou ik iets à la Sendy hierboven doen: eerst controleren of je al op het maximum zit, en zo ja, uit de lus breken, en anders de index verhogen. Mooie code levert dat niet op, maar 't is wel correct.

edit:
Sendy schreef op maandag 27 augustus 2012 @ 14:57:
Aloys heeft 't natuurlijk ook goed.
Als je met "goed" bedoelt dat beide fragmenten fout zijn, wel ja.

Volgens mij zij van de negen oplossingen gepost vóór Sendy er acht fout (de TS niet meegeteld, want die opende het topic nu juist omdat hij zich realiseerde dat z'n aanpak niet werkt). Ik geloof dat alleen HuHu een correcte oplossing postte.

[ Voor 19% gewijzigd door Soultaker op 27-08-2012 15:53 ]


Acties:
  • 0 Henk 'm!

  • Mijzelf
  • Registratie: September 2004
  • Niet online
Soultaker schreef op maandag 27 augustus 2012 @ 15:34:
Belangrijk is om te onthouden dan in C/C++ signed integers niet mogen wrappen, maar unsigned integers wel.
Hè? Wat voor waarde zou i dan moeten krijgen?
C:
1
2
int i=INT_MAX;
i++;

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
-MAX

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
Soultaker schreef op maandag 27 augustus 2012 @ 15:34:
[...]

Als je met "goed" bedoelt dat beide fragmenten fout zijn, wel ja.
Ik tikte dit voordat Aloys de tweede oplossing postte, dus ik richtte me alleen op de eerste. Verder wil ik me excuseren dat ik geen C programmeer en dus niet echt op de C signed/unsigned types gelet hebt. :)

Ik codeer HLASM op z/OS en daar hebben we types X (unsigned byte), H (singed halfword), F (signed fullword), A (unsigned fullword), en zo nog een paar.

[ Voor 16% gewijzigd door Sendy op 27-08-2012 16:04 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Mijzelf schreef op maandag 27 augustus 2012 @ 15:57:
[...]
Hè? Wat voor waarde zou i dan moeten krijgen?
C:
1
2
int i=INT_MAX;
i++;
Nee, "undefined". Dat het in de praktijk best INT_MIN zou kunnen zijn is leuk en aardig, maar daar kun je niet op vertrouwen. Een volgende versie van je compiler kan het heel anders implementeren.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Soultaker schreef op maandag 27 augustus 2012 @ 15:34:
[...]

Belangrijk is om te onthouden dan in C/C++ signed integers niet mogen wrappen, maar unsigned integers wel.
Ik snap wat je bedoelt, maar je formuleert het wat krom. Natuurlijk mogen signed ints wel wrappen. Waar het om gaat is dat het niet gegarandeerd is dat het gebeurt, itt bij unsigned integers.

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.


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
Verwijderd schreef op maandag 27 augustus 2012 @ 16:10:
Nee, "undefined". Dat het in de praktijk best INT_MIN zou kunnen zijn is leuk en aardig, maar daar kun je niet op vertrouwen. Een volgende versie van je compiler kan het heel anders implementeren.
Tuurlijk. Maar we hebben het hier over C/C++ en dat is wat er meestal gebeurt. Ik zie ook weinig reden om hierop te 'vertrouwen'. Maargoed. Ik heb nog steeds geen antwoord op de vraag wat nu precies het doel van dit topic is.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Maar je vraag is ook gewoon raar
Hydra schreef op maandag 27 augustus 2012 @ 14:57:
Ik snap alleen niet helemaal waarvoor je nu een oplossing aan het zoeken bent. Als je een array alloceert met grootte N items zal deze lopen tot index N-1. Als je 'em dus alloceert met 2^32 zal de hoogste index 2^32 -1 zijn.
En dus heb je het probleem waar de TS tegenaan loopt.

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.


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
.oisyn schreef op maandag 27 augustus 2012 @ 16:23:
En dus heb je het probleem waar de TS tegenaan loopt.
Welk probleem? Je hebt geen index N dus waarom zou je wat met die index moeten doen?

https://niels.nu


Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
Hydra schreef op maandag 27 augustus 2012 @ 16:31:
[...]


Welk probleem? Je hebt geen index N dus waarom zou je wat met die index moeten doen?
Je kan niet met een uint32_t of size_t van 32-bits een loop maken die van 0 tot en met UINT32_MAX loopt, terwijl ik wel (UINT32_MAX+1) elementen heb. Het kromme is dat je uint32_t wel genoeg waarden kan addresseren, maar je niet met dat datatype kan loopen. Althans, niet op een 'nette' manier.

[ Voor 23% gewijzigd door raoulduke op 27-08-2012 16:49 ]

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Het gaat niet om index N, het gaat om index N-1. Je int loopt maar tot N-1. UINT8_MAX is 255, niet 256.

[ Voor 15% gewijzigd door .oisyn op 27-08-2012 16:45 ]

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.


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 18:51
.oisyn schreef op maandag 27 augustus 2012 @ 16:14:
Ik snap wat je bedoelt, maar je formuleert het wat krom. Natuurlijk mogen signed ints wel wrappen. Waar het om gaat is dat het niet gegarandeerd is dat het gebeurt, itt bij unsigned integers.
Ik bedoel: je mag als programmeur je integers niet laten wrappen. In ieder geval niet zonder platform/compiler-specifieke aannames te doen. (GCC heeft een -fwrapv optie bijvoorbeeld, waarmee je gegarandeerd dezelfde wrapping semantics krijgt als bijvoorbeeld in Java. Maar da's geen standaard C garantie.)
Mijzelf schreef op maandag 27 augustus 2012 @ 15:57:
[...]
Hè? Wat voor waarde zou i dan moeten krijgen?
C:
1
2
int i=INT_MAX;
i++;
Dat kan van alles zijn. De waarde kan clippen, de waarde kan wrappen, je applicatie kan crashen vanwege een overflow-signal, of er kan een waarde uitkomen die buiten het bereik van een int ligt, omdat de compiler de variabele toevallig in een register opslaat die breder is dan een int.

Dit klinkt misschien pedantisch, maar ook op gangbare platforms als bijvoorbeeld x86 komt dat voor, vooral bij gebruik van signed integers kleiner dan een int (zoals short of signed char). Bijvoorbeeld een programma als dit:

C:
1
2
3
   short i;
   for (i = 0; i >= 0; ++i)
          printf("%d\n", i);

... print doodleuk allerlei getallen buiten het bereik van een short (als ik met GCC -O2 compileer).

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
.oisyn schreef op maandag 27 augustus 2012 @ 16:45:
Het gaat niet om index N, het gaat om index N-1. Je int loopt maar tot N-1. UINT8_MAX is 255, niet 256.
Maar hoe zou je ooit een array kunnen allocaten die groter is dan het grootste getal dat je 'systeem' aankan?

https://niels.nu


Acties:
  • 0 Henk 'm!

  • RayNbow
  • Registratie: Maart 2003
  • Laatst online: 18:38

RayNbow

Kirika <3

Hydra schreef op maandag 27 augustus 2012 @ 17:33:
[...]


Maar hoe zou je ooit een array kunnen allocaten die groter is dan het grootste getal dat je 'systeem' aankan?
Het gaat niet om het allocaten, het gaat erom dat je goed moet opletten op de luscondities.

Ipsa Scientia Potestas Est
NNID: ShinNoNoir


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
RayNbow schreef op maandag 27 augustus 2012 @ 17:38:
Het gaat niet om het allocaten, het gaat erom dat je goed moet opletten op de luscondities.
Dat is mijn vraag niet; hoe zou je ooit in een situatie kunnen komen waarbij een array groter te allocaten is dan de maximale grote van een integer waarmee je de array geallocate hebt?

https://niels.nu


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Hydra schreef op maandag 27 augustus 2012 @ 17:33:
[...]


Maar hoe zou je ooit een array kunnen allocaten die groter is dan het grootste getal dat je 'systeem' aankan?
Wie heeft het erover een array te allocaten die groter is dan je systeem aankan? Het voorbeeld gebruikte een uint8_t. Wie heeft het trouwens überhaupt over allocatie gehad? Jij bent de enige.
Hydra schreef op maandag 27 augustus 2012 @ 17:41:
[...]


Dat is mijn vraag niet; hoe zou je ooit in een situatie kunnen komen waarbij een array groter te allocaten is dan de maximale grote van een integer waarmee je de array geallocate hebt?
Misschien moet je dergelijke compleet ongerelateerde vragen in een apart topic stellen dan? :)

[ Voor 41% gewijzigd door .oisyn op 27-08-2012 17:44 ]

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.


Acties:
  • 0 Henk 'm!

  • Aloys
  • Registratie: Juni 2005
  • Niet online
@iedereen dit op mijn code reageerde... Ik had uiteraard aan moeten geven dat het om een unsigned type moet gaan bij het tweede voorbeeld. Ik wil echter vaak sneller posten dan nadenken :+ . Het eerste voorbeeld moet volgens mij ook gewoon werkend met signed integers.
Aloys schreef op maandag 27 augustus 2012 @ 14:51:
Met een do while moet het wel degelijk kunnen hoor

C:
1
2
3
4
int index = 0;
do {
   iets(index);
} while(index < intmax && index++)

Volgens mij kom je dan ook alle situaties tegen.

Volgens mij gaat dit ook goed:
C:
1
2
3
4
int index = 0;
do {
   iets(index);
} while(++index)

Als je nu terug komt bij 0 stopt de lus. :)

Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
Hydra schreef op maandag 27 augustus 2012 @ 17:33:
[...]


Maar hoe zou je ooit een array kunnen allocaten die groter is dan het grootste getal dat je 'systeem' aankan?
Omdat je (of ik in mijn geval) wel gewoon een size_t (gangbaar 32- of 64-bits) gebruikt bij het alloceren van een array en die daarna met een kleiner datatype als een uint8_t addresseert?

Stel dat je bijvoorbeeld 3D coordinaten hebt in de vorm van:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdint.h>

struct Coordinaat {
private:
  uint8_t x;
  uint8_t y;
  uint8_t z;

public:
  Coordinaat(uint8_t x, uint8_t y, uint8_t z) {
    this->x = x;
    this->y = y;
    this->z = z;
  }
}


Je zou dan 'perfect' een loop kunnen maken die het volgende doet:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "Coordinaat.h"
#include <list>

int main(int argc, char** argv) {
  std::list<Coordinaat> coordinaten;

  for (uint8_t x = 0; x <= UINT8_MAX; x++) {
    for (uint8_t y = 0; y <= UINT8_MAX; y++) {
      for (uint8_t z = 0; z <= UINT8_MAX; z++) {
        coordinaten.push_back(Coordinaat(x, y, z));
      }
    }
  }
}


Maar heel deze code werkt dus niet. Een ander datatype voor de loop-index gebruiken houdt in dat je veel moet gaan casten en daar wordt de code niet duidelijker op. Het loopen t/m (UINT8_MAX - 1) en 0 edge-case'n gaat ook lastig. Vandaar dus mijn vraag.

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

Aloys schreef op maandag 27 augustus 2012 @ 17:43:
Het eerste voorbeeld moet volgens mij ook gewoon werkend met signed integers.
Niet dus. Het is UB. In de praktijk werkt het vaak wel omdat een signed int naar een negatief getal wrapt, maar dat hoeft dus niet.

[ Voor 21% gewijzigd door .oisyn op 27-08-2012 17:45 ]

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.


Acties:
  • 0 Henk 'm!

  • Aloys
  • Registratie: Juni 2005
  • Niet online
.oisyn schreef op maandag 27 augustus 2012 @ 17:44:
[...]

Niet dus. Het is UB. In de praktijk werkt het vaak wel omdat een signed int naar een negatief getal wrapt, maar dat hoeft dus niet.
Ah, ik ging er vanuit dat dat dus altijd gebeurd in C. Ik heb nog nooit zo'n overflow gehad, goed om te weten aangezien ik ook wel eens naar andere platformen moet porteren :) .

Edit: UB = Undefined Behaviour?

Edit2.. dan nog steeds moet het volgens mij werken. De lus gaat immers door totdat er in de praktijk INT_MAX < INT_MAX staat en dan stopt ie. Je hoeft dan nooit het teken om te laten klappen?

[ Voor 21% gewijzigd door Aloys op 27-08-2012 17:49 ]


Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
Ik snap 't niet meer. Dat eerste voorbeeld van Aloys werkt toch wel? Er wordt daar nooit gewrapt... ?!

Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
Sendy schreef op maandag 27 augustus 2012 @ 14:57:
Ik heb zo'n fout ook al eens in het slechte programmeervoorbeelden topic gepost.
Pseudo code, en omdat dit zo simpel is mag je zelf bedenken of dat nu do while, do until, of een mix dit is.
Tot nu toe ben ik het meeste fan van deze oplossing, maar dan iets verbasterd:
C++:
1
2
3
4
for (uint8_t index = 0; ; index++) {
  process(index);
  if (UINT8_MAX == index) break;
}


Die loop vertrouwt op geen enkele manier op wrapping (alhoewel dat volgens mij wel is gestandaardiseerd in C++) en ziet er in mijn ogen goed leesbaar uit.

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • Waster
  • Registratie: September 2006
  • Laatst online: 14-04 17:49
Ik vind deze wel duidelijk. Je begint bij 0 en als hij weer 0 wordt na een volledige iteratie dan stop je.

C++:
1
2
3
4
5
int index = 0;
do {
    process(index);
    index++;
} while ( index != 0 );

Acties:
  • 0 Henk 'm!

Verwijderd

Waster schreef op maandag 27 augustus 2012 @ 18:28:
Ik vind deze wel duidelijk. Je begint bij 0 en als hij weer 0 wordt na een volledige iteratie dan stop je.

C++:
1
2
3
4
5
int index = 0;
do {
    process(index);
    index++;
} while ( index != 0 );
Lees de posts boven je nog eens goed door. Omdat je een int gebruikt is het niet gegarandeerd dat hij ooit weer 0 wordt.

Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

ISO C++11 standaard, 3.9.1.4:
Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer. [46]
Voetnoot 46:
This implies that unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type.
Dus voor unsigned types is dit altijd defined. Voor de rest is op systemen met twos-complement representatie het gedrag meestal hetzelfde als voor unsigned, maar dat is niet standaard gedefinieerd.

Maar je kan ook prima voorkomen dat er ooit overflow optreed:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <limits>
#include <type_traits>

template<typename T, typename Callable>
void foreach_int(Callable call)
{
  static_assert(std::is_integral<T>::value, "T has to be an integral type");
  T t = std::numeric_limits<T>::min();
  while(1)
  {
    call(t);
    if(t == std::numeric_limits<T>::max()) break;
    ++t;
  }
}

#include <iostream>
int main()
{
  foreach_int<short>([](short val){ std::cout << val << std::endl; });
  return 0;
}


Voorbeeld van uitvoer: http://ideone.com/Gndjl (voor char ipv short, anders krijg je wel enorme spam in je resultaten :P)

[ Voor 29% gewijzigd door MLM op 27-08-2012 18:56 ]

-niks-


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 18:51
Sendy schreef op maandag 27 augustus 2012 @ 17:49:
Ik snap 't niet meer. Dat eerste voorbeeld van Aloys werkt toch wel? Er wordt daar nooit gewrapt... ?!
Nee, maar er wordt ook niet meer dan één iteratie van de lus uitgevoerd. ;)

Acties:
  • 0 Henk 'm!

  • Aloys
  • Registratie: Juni 2005
  • Niet online
Soultaker schreef op maandag 27 augustus 2012 @ 18:47:
[...]

Nee, maar er wordt ook niet meer dan één iteratie van de lus uitgevoerd. ;)
Scherp, daar had ik niet aan gedacht! :)

Dat zou overigens wel opgelost zijn door index++ te vervangen door ++index :) .

[ Voor 14% gewijzigd door Aloys op 27-08-2012 18:55 ]


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
raoulduke schreef op maandag 27 augustus 2012 @ 17:44:
Omdat je (of ik in mijn geval) wel gewoon een size_t (gangbaar 32- of 64-bits) gebruikt bij het alloceren van een array en die daarna met een kleiner datatype als een uint8_t addresseert?
Dus je gaat complexe constructies gebruiken in plaats van dat ding gewoon met hetzelfde type te adresseren?

Hookay :?

https://niels.nu


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-09 21:24

.oisyn

Moderator Devschuur®

Demotivational Speaker

En het komt echt niet bij je op dat dit een simpel voorbeeld is, maar dat je in het echt een dergelijke index ook opslaat in een datastructuur en er daardoor voor kiest om een zo klein mogelijke representatie van de index kiest om geheugen te besparen?

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.


Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
Soultaker schreef op maandag 27 augustus 2012 @ 18:47:
[...]

Nee, maar er wordt ook niet meer dan één iteratie van de lus uitgevoerd. ;)
Ah, ik zie 't. Thanks. :)

[ Voor 3% gewijzigd door Sendy op 28-08-2012 01:10 ]


Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

.oisyn schreef op maandag 27 augustus 2012 @ 23:59:
En het komt echt niet bij je op dat dit een simpel voorbeeld is, maar dat je in het echt een dergelijke index ook opslaat in een datastructuur en er daardoor voor kiest om een zo klein mogelijke representatie van de index kiest om geheugen te besparen?
Ik vraag me werkelijk af of het de moeite waard is om bij een dergelijke index een zo klein mogelijk datatype te gebruiken om geheugen te besparen. Naast de ellende die je je op de hals haalt (zoals dit topic duidelijk laat zien) is het nog maar de vraag of er werkelijk geheugen bespaart wordt (memory alignment / padding etc).
Als dit de de enige reden zou zijn dan is het een overduidelijk voorbeeld van premature optimization imho. De vraag van Hydra vind ik dan ook een hele logische om te stellen.

Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
.oisyn schreef op maandag 27 augustus 2012 @ 23:59:
En het komt echt niet bij je op dat dit een simpel voorbeeld is, maar dat je in het echt een dergelijke index ook opslaat in een datastructuur en er daardoor voor kiest om een zo klein mogelijke representatie van de index kiest om geheugen te besparen?
+1

Alhoewel het een leuk idee is om bijvoorbeeld een size_t te gebruiken om de code niet 'complex' te maken (en ik ben het niet eens met die classificatie van de -in mijn ogen volstrekt leesbare- voorgestelde loopaanpassing), blijkt dat je dan in de praktijk opeens veel nadelen ondervindt:
  • potentieel 8x (64-bit size_t op x86_64 vs 8-bit uint8_t) meer RAM in gebruik
  • potentieel 8x meer load op de geheugenbus
  • potentieel 8x meer cache-trashing omdat er minder data per geheugenpagina in de cache past
  • potentieel meer CPU gebruik, omdat je een struct van 3x8-bit al in één 32-bit register op IA32 zou kunnen stoppen en dat met 3x64-bit wat lastiger wordt
  • potentieel meer CPU-gebruik, omdat deze waarden ook over het netwerk worden verstuurd en dan óf iedere keer gecast moeten worden van size_t naar uint8_t, óf je krijgt 4x zoveel eigen bytes te versturen
Hydra, ik zou die lijst nog verder kunnen uitwerken en ik weet zeker dat iemand die lijst zonder problemen zou kunnen afdoen als onzin, maar ik denk dat we misschien moeten accepteren dat we het fundamenteel oneens zijn over wat voor keuzes soms gerechtvaardigd zijn.

Begrijp me goed dat ik een gigantisch voorstander ben van heldere, goedgeschreven code en probeer zo veel mogelijk programmeerfouten uit te sluiten door goede richtlijnen aan te houden, maar ik vind het kiezen en gebruiken van het 'juiste' datatype ook vallen onder 'goede' code.

Omdat ik niet eerder tegen het gekke probleem van dit topic aanliep -want meestal loop je inderdaad tot en met [N-1] in een array- greep ik de kans aan om wat te leren in plaats van de (in mijn ogen) foute keuze te maken en te gaan casten naar ongerelateerde, grotere datatypes ten koste van de leesbaarheid en performance van mijn code. Hopelijk is dat te begrijpen.

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • EddoH
  • Registratie: Maart 2009
  • Niet online

EddoH

Backpfeifengesicht

Niet om een hele lange discussie uit te lokken, just curious: heb je het verschil wel eens gebenchmarked?

getriggered door het woordje 'potentieel'

[ Voor 22% gewijzigd door EddoH op 28-08-2012 09:46 ]


Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
EddoH schreef op dinsdag 28 augustus 2012 @ 09:45:
Niet om een hele lange discussie uit te lokken, just curious: heb je het verschil wel eens gebenchmarked?

getriggered door het woordje 'potentieel'
Oh, 'potentieel' gebruik ik omdat ik me net als vele anderen realiseer dat enige winst per punt totaal afhankelijk is van de gebruikte compiler, settings, platform, word-size en de temperatuur op Antarctica. Toch zijn het in mijn ogen theoretisch valide argumenten die op zichzelf overtuigend zijn en met z'n allen gecombineerd helemaal overtuigingskracht bezitten, feel free to disagree.

Heb ik gebenchmarked? Dat is een makkelijk te stellen vraag om de tijdsintensieve bewijslast bij iemand anders neer te leggen :) Ik heb niet de tijd of de zin om mijn hele codebase om te gaan zetten en te benchmarken, want dan hebben we nog steeds vervuilde resultaten door andere berekeningen.

Uit nieuwsgierigheid zou ik wel een ietwat kunstmatige testcase willen opzetten om te zien of daar verschil in te merken is.

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • __fred__
  • Registratie: November 2001
  • Laatst online: 16:32
http://www.eventhelix.com...r int over char and short

Als je char of short onderdeel is van een struct ok, maar als teller variabele of functieargumenten lijkt het me zinloos.

[ Voor 40% gewijzigd door __fred__ op 28-08-2012 10:06 ]


Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
raoulduke schreef op dinsdag 28 augustus 2012 @ 09:56:
[...]
Uit nieuwsgierigheid zou ik wel een ietwat kunstmatige testcase willen opzetten om te zien of daar verschil in te merken is.
OK, ik heb mijn 3D-coordinaten-voorbeeld uit dit topic gebruikt om te benchmarken, aangezien dat representatief is voor mijn vraag. Als eerste de code, zodat iedereen zelf kan testen:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <list>
#include <stdint.h>
#include <stdio.h>
#include <sys/time.h>

template <typename ELEMENT_T> struct Vector3 {
    ELEMENT_T x, y, z;
    
    Vector3(ELEMENT_T x, ELEMENT_T y, ELEMENT_T z) {
        this->x = x;
        this->y = y;
        this->z = z;
    }
};

typedef Vector3<uint8_t> Byte3;
typedef Vector3<uint32_t> UInt3;

void testBytes();
void testUInts();
uint64_t getCurrentTimeInUs();

int main (int argc, char** argv) {
    uint64_t bytesT0 = getCurrentTimeInUs();
    testBytes();
    uint64_t bytesTime = getCurrentTimeInUs() - bytesT0;
    printf("Test bytes took %llu us\n", bytesTime);
    
    uint64_t uintsT0 = getCurrentTimeInUs();
    testUInts();
    uint64_t uintsTime = getCurrentTimeInUs() - uintsT0;
    printf("Test unsigned integers took %llu us\n", uintsTime);
    
    return 0;
}

void testBytes() {
    std::list<Byte3>* listBytes = new std::list<Byte3>();
    for (uint8_t x = 0; ; x++) {
        for (uint8_t y = 0; ; y++) {
            for (uint8_t z = 0; ; z++) {
                listBytes->push_back(Byte3(x, y, z));
                if (UINT8_MAX == z) break;
            }
            if (UINT8_MAX == y) break;
        }
        if (UINT8_MAX == x) break;
    }
}

void testUInts() {
    std::list<UInt3>* listUInts = new std::list<UInt3>();
    for (uint32_t x = 0; x <= UINT8_MAX; x++) {
        for (uint32_t y = 0; y <= UINT8_MAX; y++) {
            for (uint32_t z = 0; z <= UINT8_MAX; z++) {
                listUInts->push_back(UInt3(x, y, z));
            }
        }
    }
}

uint64_t getCurrentTimeInUs() {
    timeval tv;
    gettimeofday(&tv, 0);
    return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec;
}


Merk op dat ik met 32-bit unsigned integers test, omdat ik zelf nog op een wat oudere 32-bit x86 machine werk. Met 64-bit unsigned integers testen zou de test oneerlijk maken wat mij betreft, dus ik test het best-case scenario voor het grotere datatype. Code is geschreven en getest op Mac OS X 10.6.8. In 'main' zet ik steeds slechts testBytes() of testUInts() aan, anders heeft een eerdere test invloed op de tweede.

De looptijden zijn als volgt (systeem is zo goed mogelijk in idle staat gebracht):
compiler + flagstestBytes (us)testUInts (us)factor
llvm-g++506422555484851.10x
llvm-g++ -O3229806028540621.24x
clang++482542153774221.11x
clang++ -O4231082128184941.22x


Het geheugengebruik is als volgt ('Real Mem' gemeten in Activity Monitor fwiw):
compiler + flagstestBytes (MB)testUInts (MB)factor
llvm-g++2605202x
llvm-g++ -O32605202x
clang++2605202x
clang++ -O42605202x


Als laatste heb ik nog getest of er verschil is tussen loopen met een uint8_t of een uint32_t in testBytes(), maar daar komen te kleine verschillen uit om iets nuttigs te zeggen. Datatype binnen de struct maakt in de bovenstaande test dus het grootste verschil; de loop en het eventueel casten van datatypes lijkt van ondergeschikt belang.

[ Voor 4% gewijzigd door raoulduke op 28-08-2012 11:41 ]

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

2x memory is makkelijk te verklaren: jouw std::list gebruikt de heap allocator, en desktop OS-en gebruiken allemaal (vziw) 16byte aligned heap memory.

Geval std::list<vec3<uint8_t>> is per item 11 bytes (3 voor de vec3, en 8 voor de prev/next pointers in de list node), dat past in de 16 bytes.
Geval std::list<vec3<uint32_t>> is per item 20 bytes (12 voor de vec3, en 8 voor prev/next pointers in de list node), dat past niet in de 16 bytes (en word dus 32).

Et voila, dubbel geheugen gebruik :) (Exercise for the reader, geheugengebruik op 64 bit systeem gaat gelijk zijn in beide gevallen, waarom? :P)

Gebruik een std::vector om dat probleem op te lossen (ik voorspel 64MB en 256MB als je niet swap-trickt, 48MB en 192MB als je het wel doet). Voor bonuspunten, resize je vector vooraf, en het enige wat je gaat meten is je memory fill speed, en dan gaat het geval voor uint8_t echt 3x sneller zijn dan uint32_t als je compiler kan optimizen :) (of als je draait op x86 of ander moderne CPU, ga je toch write-combines krijgen, en maakt het mogelijk niet eens uit of je code optimized is of niet)

Als sidenote: een datastructuur van opeenvolgende integers (al dan niet in een vec3) is ENORM waardeloos. Die kan je ook opslaan als 2 words (eerste int, laatste int). In het specifieke geval van complete range van welk integral type dan ook, is het zelfs impliciet en heb je 0 bytes storage nodig :) Dit alles omdat je de waarde op elke index in O(1) kan genereren aan de hand van de index (en andersom), dus onnodig ;)

[ Voor 17% gewijzigd door MLM op 28-08-2012 12:18 ]

-niks-


Acties:
  • 0 Henk 'm!

  • Zeebonk
  • Registratie: Augustus 2005
  • Laatst online: 30-07 20:50
Ik zie niet in waarom er voor een unsigned 32 bits int is gekozen. Het argument ging over premature optimization en simpliciteit, "int" lijkt mij dan veel toepasselijker dan "uint32_t":

g++ test.c
Test bytes took 2172473 us
Test int took 2026513 us

g++ test.c -O3
Test bytes took 1150683 us
Test int took 1069682 us

[ Voor 13% gewijzigd door Zeebonk op 28-08-2012 12:05 ]


Acties:
  • 0 Henk 'm!

  • Big Womly
  • Registratie: Oktober 2007
  • Laatst online: 01-09 13:39

Big Womly

Live forever, or die trying

raoulduke schreef op maandag 27 augustus 2012 @ 18:01:
[...]


Tot nu toe ben ik het meeste fan van deze oplossing, maar dan iets verbasterd:
C++:
1
2
3
4
for (uint8_t index = 0; ; index++) {
  process(index);
  if (UINT8_MAX == index) break;
}


Die loop vertrouwt op geen enkele manier op wrapping (alhoewel dat volgens mij wel is gestandaardiseerd in C++) en ziet er in mijn ogen goed leesbaar uit.
Dit leesbaar? Er ontbreekt een voorwaarde in je for lus wanneer de iteraties moet stoppen en ergens onderwegen je lus plaats je dan de code om je iteraties af te breken. Allesbehalve leesbaar als je het mij vraagt.
Nu valt het al bij al nog mee omdat het een kleine lus is, maar bij complexere constructies is dit gewoon not done.

When you talk to God it's called prayer, but when God talks to you it's called schizophrenia


Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
.oisyn schreef op maandag 27 augustus 2012 @ 23:59:
En het komt echt niet bij je op dat dit een simpel voorbeeld is, maar dat je in het echt een dergelijke index ook opslaat in een datastructuur en er daardoor voor kiest om een zo klein mogelijke representatie van de index kiest om geheugen te besparen?
Mag ook wel iets vriendelijker. Mijn punt is dat je dergelijke rare constructies gewoon moet proberen te vermijden indien mogelijk, en als je het echt niet kunt vermijden de oplossing gewoon doodsimpel is. Zelfs als je in een datastructuur perse een byte wil gebruiken kun je die in een loop prima casten naar int oid.

Vandaar dat ik het nut van dit topic niet zie. Het is een probleem dat OF niet voorkomt (omdat je toch niet groter kunt alloceren), OF simpel te fixen is door in een loop te casten naar een 'normaal' datatype.
raoulduke schreef op dinsdag 28 augustus 2012 @ 09:38:
Hydra, ik zou die lijst nog verder kunnen uitwerken en ik weet zeker dat iemand die lijst zonder problemen zou kunnen afdoen als onzin, maar ik denk dat we misschien moeten accepteren dat we het fundamenteel oneens zijn over wat voor keuzes soms gerechtvaardigd zijn.
Ik zeg niet dat het onzin is, ik vind het nutteloos, dat is wat anders. Een array van 2^32 items moeten indexeren met een 'byte' is een compleet randgeval. Het probleem is IMHO uberhaupt niet de for-loop: het probleem is dat je kennelijk ruimte probeert te besparen in je geheugen en dat in die 'byte' toch waarden hoger dan 255 moeten. Tja, dergelijke randgevallen zijn IMHO geen topic waardig. Daar kan iedere zichzelf respecterende software developer wel een oplossing voor bedenken medunkt.

[ Voor 32% gewijzigd door Hydra op 28-08-2012 12:28 ]

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Zeebonk
  • Registratie: Augustus 2005
  • Laatst online: 30-07 20:50
Hydra zijn opmerking over casten toegepast: int casten naar uint8_t:

Test bytes took 2148670 us
Test ints took 2136409 us

Met geheugengebruik voordeel zoals beschreven door MLM

Acties:
  • 0 Henk 'm!

  • pedorus
  • Registratie: Januari 2008
  • Niet online
MLM schreef op dinsdag 28 augustus 2012 @ 12:02:
Als sidenote: een datastructuur van opeenvolgende integers (al dan niet in een vec3) is ENORM waardeloos. Die kan je ook opslaan als 2 words (eerste int, laatste int). In het specifieke geval van complete range van welk integral type dan ook, is het zelfs impliciet en heb je 0 bytes storage nodig :) Dit alles omdat je de waarde op elke index in O(1) kan genereren aan de hand van de index (en andersom), dus onnodig ;)
Dit natuurlijk. Als dit Java was geweest, dan had ik er nog iets van begrepen, maar ik snap eigenlijk niet voor welk probleem bovenstaande 'oplossingen' een oplossing zijn. De benchmarks komen mij ook zeer theoretisch voor, aangezien ik mij geen zinnig probleem kan voorstellen dat gelijkenis vertoont. ;)

Vitamine D tekorten in Nederland | Dodelijk coronaforum gesloten


Acties:
  • 0 Henk 'm!

  • riemer1990
  • Registratie: Juni 2010
  • Laatst online: 12:33
Zou je niet uint_8 kunnen gebruiken in de datatypes, en een normale int voor de teller in de loop?
Zoveel zou dat niet moeten schelen, en bespaart je van moeilijkere code.

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 21-08 17:09
riemer1990 schreef op dinsdag 28 augustus 2012 @ 12:48:
Zou je niet uint_8 kunnen gebruiken in de datatypes, en een normale int voor de teller in de loop?
Zoveel zou dat niet moeten schelen, en bespaart je van moeilijkere code.
Dat zeg ik. Aangezien je ALU AFAIK intern toch 32/64 bits gebruikt voor de instructies maakt 't volgens mij geen hout uit.

[ Voor 15% gewijzigd door Hydra op 28-08-2012 12:54 ]

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Zeebonk
  • Registratie: Augustus 2005
  • Laatst online: 30-07 20:50
En heb ik net getest

Acties:
  • 0 Henk 'm!

  • raoulduke
  • Registratie: Oktober 2003
  • Niet online
pedorus schreef op dinsdag 28 augustus 2012 @ 12:40:
[...]
Dit natuurlijk. Als dit Java was geweest, dan had ik er nog iets van begrepen, maar ik snap eigenlijk niet voor welk probleem bovenstaande 'oplossingen' een oplossing zijn. De benchmarks komen mij ook zeer theoretisch voor, aangezien ik mij geen zinnig probleem kan voorstellen dat gelijkenis vertoont. ;)
Klopt helemaal hoor, vandaar dat ik vooraf al sprak van een kunstmatige benchmark. Het scenario is dan ook niet letterlijk de productiecode in mijn programma; dat zou wat zijn!

Sowieso geldt: als ik een std::vector gebruik (met toevoeging van lege constructor in Byte3) in plaats van std::list, zakt het geheugengebruik naar ongeveer 55 MB èn de looptijd (g++ -O3) zakt naar 313 ms in plaats van 2000+ ms. Dat maakte het bikeshedden over andere zaken eigenlijk al nutteloos, maar goed - al doende leert men.
riemer1990 schreef op dinsdag 28 augustus 2012 @ 12:48:
Zou je niet uint_8 kunnen gebruiken in de datatypes, en een normale int voor de teller in de loop?
Zoveel zou dat niet moeten schelen, en bespaart je van moeilijkere code.
Dat is denk ik het takeaway point van dit topic geworden; alhoewel het een grappige uitdaging was om te kijken hoe je een loop kan maken die alle waardes van een datatype langsloopt met een index van dat datatype, is het werken met een doodgewone int en casten naar kleinere datatypes niet per definitie langzamer dan de 'correcte' datatypes gebruiken.

Mijn 'benchmark' hierboven test bij nader inzien eigenlijk ook twee irrelevante scenario's, maar geeft aan dat het wel enigszins verschil maakt of je data opslaat als uint8_t of uint32_t, maar dat het niet echt uitmaakt of je eroverheen loopt met een gecaste int.

Alhoewel ik nog even moet wennen aan het expliciet casten van waardes, heeft het gebruiken van 'normale' int-loopjes nu mijn voorkeur boven de alternatieve for-syntax die ik eerder postte. Ik moet alleen nog even wennen aan het idee dat een int potentieel buiten het bereik van het doel-datatype kan vallen...

Maar goed, al met al; Hydra (e.a.): jouw/jullie 'common sense' voorstel is een goede oplossing.

Remember, if you have any trouble you can always send a telegram to the Right People.


Acties:
  • 0 Henk 'm!

  • Aloys
  • Registratie: Juni 2005
  • Niet online
Achteruit lopen is ook leuk:
C:
1
2
3
4
5
6
7
8
9
uint8_t index = UINT8_MAX;

/* index max t/m 1 */
do {
   processSomething(index);
} while(0 <-- index);

/* index 0 */
processSomething(0);


Vooruit op dezelfde mooie notatie (wel met gebruikt van het het terugkeren naar 0):
C:
1
2
3
4
5
6
uint8_t index = 0;

/* index 0 t/m UINT8_MAX   */
do {
   processSomething(index);
} while(0 <++ index);


De enige reden om dit te posten was natuurlijk de pijltje notatie: <-- en <++ :+ .

Acties:
  • 0 Henk 'm!

  • sam.vimes
  • Registratie: Januari 2007
  • Laatst online: 08-06 08:44
C++:
1
2
3
4
for (uint8_t index = 0; ; index++) {
  process(index);
  if (UINT8_MAX == index) break;
}
Big Womly schreef op dinsdag 28 augustus 2012 @ 12:20:
[...]
Dit leesbaar? Er ontbreekt een voorwaarde in je for lus wanneer de iteraties moet stoppen en ergens onderwegen je lus plaats je dan de code om je iteraties af te breken. Allesbehalve leesbaar als je het mij vraagt.
Nu valt het al bij al nog mee omdat het een kleine lus is, maar bij complexere constructies is dit gewoon not done.
Juist doordat de eindvoorwaarde van de for ( ; ; ) zo overduidelijk ontbreekt, weet je dat er ergens in de lus een of meer breaks moeten staan. Ik gebruik dit zelf vooral in lussen die meerdere exitvoorwaarden hebben, in plaats van die allemaal in de 2de expressie van het for-statement te proppen.
Ik geef daarom ook de voorkeur aan
C++:
1
2
3
for ( ; ; ) {
  blurb
}

boven
C++:
1
2
3
while (1) {
  blurb
}

[ Voor 194% gewijzigd door sam.vimes op 28-08-2012 13:58 ]


Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
Want aan while (1) kan je niet ziet dat er expliciet uit de loop gebrancheted moet worden?

Acties:
  • 0 Henk 'm!

  • narotic
  • Registratie: Maart 2002
  • Laatst online: 02-11-2021
code:
1
2
3
for (uint8_t index = 0, prev = 0; index >= prev; prev = index, index++) {
  process(index);
}


De leesbaarheid is niet echt top, maar het werkt voor elke integer type zonder expliciet de het maximum op te hoeven geven.

- = Step Into The Pit | Industrial Strength = -


Acties:
  • 0 Henk 'm!

  • leuk_he
  • Registratie: Augustus 2000
  • Laatst online: 15-07 15:35

leuk_he

1. Controleer de kabel!

Sendy schreef op dinsdag 28 augustus 2012 @ 14:17:
Want aan while (1) kan je niet ziet dat er expliciet uit de loop gebrancheted moet worden?
While (1) geeft op MS compiler op hoog warning nivo een waarschuwing for (;;) niet.

"C4127: conditional expression is constant"

En je wil uiteraard al je warnings eruit krijgen.

Niet dat ik het ermee eens ben dat dit echt uit maakt...

Need more data. We want your specs. Ik ben ook maar dom. anders: forum, ff reggen, ff topic maken
En als je een oplossing hebt gevonden laat het ook ujb ff in dit topic horen.


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
[oud]

[ Voor 94% gewijzigd door MSalters op 29-08-2012 21:44 ]

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

Pagina: 1