Stukje code wat ik niet helemaal begrijp

Hi Guys,

Ik heb een stukje code waarvan ik het begin niet helemaal begrijp.
Kan iemand mij daar wat wat verlichting over geven?


((x >> teller)&1) ? printf("1"):printf("0");

Dat het een vraag is waarbij positief beantwoord er "1" wordt geprint en negatief "0", was me al duidelijk.

Daarnaast heeft het iets met het shiften van bits te maken en volgens mij of er een match is met "1" (AND 1)

Tot zover. De rest is me wat onduidelijk.
anyone on this?

Ik moet hier weer vaker komen... Wat kun je zo'n forum als deze gaan missen. :-)
JoWi

Golden Member

Is bit [teller] van x een één of een nul.
Teller zal wel van 0 tot 8 of 16 lopen.

Ignorance is bliss

x >> teller -> dit schuift het getal in x, "teller" keer naar rechts.
vervolgens word alleen bit 0 bewaard.
Eigenlijk is die printf("1"):printf("0") overbodig, men had ook direct het resultaat kunnen printen.

bv. x=10100101


teller resultaat
0          1
1          0
2          1
3          0
4          0
5          1
6          0
7          1
Frederick E. Terman

Honourable Member

Ik denk dat de bitjes van 'x' naar rechts worden geschoven, 'teller' maal (wat erop neerkomt dat 'x' 'teller'-maal door twee wordt gedeeld.
(Ofwel, 'x' wordt gedeeld door [2 tot de macht 'teller'].)

De bitwise AND van de resulterende 'x' met 1 geeft de LSB van die 'x'.
Als die 1 is wordt een '1' geprint, en anders een '0'.

e: wat hij ↑ zegt, dus. ;)

Keramisch, kalibratie, parasitair: woordenlijst.org
benleentje

Golden Member

Stel je hebt het binaire getal
1111 0000
1111 0000 >> 1 = 0111 1000
1111 0000 >> 2 = 0011 1100

enz

Alle bitjes worden naar rechts verschoven en hoeveel keer dat gebeurt dat is de waarde van teller.

Zo kan je heel makkelijk een 32 bit getal maken uit 4 bytes

32 bit getal is x

X = X + byte1
X << 8 ?? alles bitjes 8x naar links

X = X + byte2
X << 8

of er een match is met "1" (AND 1)

Uit ((x >> teller)&1) moet een 1 of 0 uitkomen om zo een waar of niet waar over te houden.

[Bericht gewijzigd door benleentje op woensdag 13 november 2024 23:01:47 (16%)

Hoeben

Golden Member

Het is treurig dat programmeurs denken dat code met alles op 1 regel ook tot een meer optimaal programma leidt. Het probleem is dat door dergelijke code je bij review of wijzigen de hele tijd moet kijken 'wat doet dit ook al weer' waardoor je vertraagd wordt en overzicht verliest. Bij grotere programmas best onhandig. Voordeel is wel dat je veel op 1 pagina krijgt.

Het doel is duidelijk met de teller laten zien welk bit 0 of 1 is.

Zoals williewortel schrijft:
x >> teller, dit schuift het getal in x "teller" keer naar rechts.
&1, dit is een AND, alles weg behalve het meest rechtse bitje
?, is dit true of false (1 is true, 0 is false)
dan doe een printf("1") anders printf("0")

waar de laatste regel ook printf("een") anders printf("nul") had kunnen zijn

In pseudotaal:
if ( SHR(x,teller) &1) = 1
then print '1'
else print '0'

Arco

Special Member

Grappig toch dat je als pseudocode meestal iets basic-achtigs ziet, omdat dat tenminste te begrijpen is... :+
In dit geval is het zelfs korter en duidelijker...:


Print Str$((x>>count) And 1)
Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
JoWi

Golden Member

Dit is natuurlijk korter:

printf("%d",(x >> teller)&1);

of:

putc(((x >> teller)&1)+'0');

Maar ik ben natuurlijk gedeformeerd door 40+ jaar C/C++ programmeren op embedded spul.

Ignorance is bliss

Op donderdag 14 november 2024 08:12:50 schreef JoWi:
Dit is natuurlijk korter:

putc(((x >> teller)&1)+'0');

Kan nog korter, behalve dat putc nog een tweede argumetn (FSTREAM*) nodig heeft


putc(((x=>>1)&1)+'0', stdout);

Maar zoals altijd bij perlC-golf, de leesbaarheid wordt enkel minder.


// print all bits of x individually
for (teller = 0; teller <  sizeof(x)*8; teller++) {
    if( (x >> teller) & 1) {
        printf("1");
    } else {
        printf("0");
    }
}

Als ik nu eens bovenstaand programmatje (met de verplichte void main() en #include boilerplate bouw, en vergelijk met deze:


// print all bits of x individually
for (teller = 0; teller <  sizeof(x)*8; teller++) {
    printf((x>>teller)&1 ? "1": "0");
}

Dan levert de eerste kleinere code op!
Link:
https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA…

Blijkbaar snapt de compiler leesbare code beter 8)7

JoWi

Golden Member

Op donderdag 14 november 2024 09:06:29 schreef blurp:
[...]
Kan nog korter, behalve dat putc nog een tweede argumetn (FSTREAM*) nodig heeft

Hangt van je compiler af, vaak is putc() een macro voor fputc().
Code wordt er niet groter of kleiner door.

>>Maar zoals altijd bij perlC-golf, de leesbaarheid wordt enkel minder.

Yep 7 regels in plaats van één. Dan is met één regel het overzicht over je code toch beter, want er staan maar een beperkt aantal regels op je beeldscherm.
En ja: ik heb een folding editor, maar dan moet ik weer op die accolade klikken.

Ignorance is bliss

Op donderdag 14 november 2024 09:06:29 schreef blurp:
Kan nog korter, behalve dat putc nog een tweede argumetn (FSTREAM*) nodig heeft

Dat is fputc, niet putc. putc(c) is equivalent met fputc (c, stdin).

Die printfs in de ? : constructie heb ik nog nooit gebruikt of gezien. Maar... moet kunnen.

Het originele stukje code is waarschijnlijk bedoeld om de waarde van x binair te printen.

for (teller=7;teller >= 0; teller--) putc ( ((x>>teller)&1)+'0');

is hoe ik het zou schrijven.

[Bericht gewijzigd door rew op donderdag 14 november 2024 10:23:37 (37%)

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

Op donderdag 14 november 2024 10:10:28 schreef JoWi:
Hangt van je compiler af, vaak is putc() een macro voor fputc().

Dan klopt er iets niet aan je compiler. De standaard is vrij duidelijk, putc en fputc hebben allebei twee argumenten. (link naar posix want ik kan zo snel geen online ISO C standaard vinden)

putchar daarintegen gebruikt er maar een.

Yep 7 regels in plaats van één. Dan is met één regel het overzicht over je code toch beter, want er staan maar een beperkt aantal regels op je beeldscherm.

Maak er dan een (desnoods inline) functie van.

JoWi

Golden Member

Op donderdag 14 november 2024 10:56:24 schreef blurp:
[...]
putchar daarintegen gebruikt er maar een.

OK.. putchar() ipv putc(), ik moet ook niet posten voor ik koffie op heb :)

Ignorance is bliss
Hensz

Golden Member

Op donderdag 14 november 2024 08:12:50 schreef JoWi:
Dit is natuurlijk korter:

Ik verbaas me al meer dan 40 jaar over al die mensen die denken dat als je minder tekst gebruikt voor je source-code je dan ook compactere object-code krijgt.
Ik spendeer liever een paar letters meer, zodat de kans dat ik (of een ander) het later begrijp groter is. Dan doet de compiler er maar 3ms langer over, ik kan er niet mee zitten. Het is ook gewoon fijn dat je de compiler kunt starten, koffie kunt halen en dat ie dan nog nét niet klaar is. :+

Ik zag maandag in de trein, naast me, iemand die in code zat te bladeren. In de gauwigheidheid spotte ik een regeltje Secretcode == Secret_code. Zó krijg je echt snel allerlei fouten en misverstanden.

Don't Panic!

Op donderdag 14 november 2024 10:56:24 schreef blurp:
putchar daarintegen gebruikt er maar een.

Ja, die bedoel ik! Sorry. foutje bedankt. :-)

Op donderdag 14 november 2024 14:30:16 schreef Hensz:
[...]
Ik verbaas me al meer dan 40 jaar over al die mensen die denken dat als je minder tekst gebruikt voor je source-code je dan ook compactere object-code krijgt.

En ik verbaas me al jaren over mensen die denken dat als je het langer opschrijft het duidelijker wordt.

Ik ben er op tegen om "teller" te schrijven. Iedereen weet dat de variabele i voor "actueel gebruik als teller in een loop" bedoeld is. Dan "teller" schrijven maakt de boel alleen maar onnodig langer en onoverzichtelijker.

Die "printf" binnen de ? : constructie is zodanig ongebruikelijk dat ik die in 35 jaar C nog niet ben tegengekomen. Dat is dus een constructie waar ik even voor moest stoppen, wat nieuws moest leren constateren dat dit ook werkt en dan pas door. Dat is NIET de bedoeling voor "duidelijke code".

Van mij mag je


   for (i=7;i>=0;i--) printf ("%c", ((x>>i)&1)?'1':'0'); 

schrijven als je dat duidelijker vind dan de '0'+ ... constructie.

Over optimalisatie: Ik doe het zeker niet zo korter om efficientere code te krijgen. Het geheel blijft overzichtelijker.

By the way... Ik heb mijn laatste code en de originele code (met een loop er omheen) in m'n compiler gestopt. Beide resulteren in een 13 instructie lange functie. (13 = incl de functie prelude en post-lude) en een 5 instructie lange loop waarbij niet printf ("%c", ...) maar putchar aangeroepen wordt!

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

Op donderdag 14 november 2024 14:30:16 schreef Hensz:
[...]
Ik verbaas me al meer dan 40 jaar over al die mensen die denken dat als je minder tekst gebruikt voor je source-code je dan ook compactere object-code krijgt.

Het leukste vind ik dus: Soms is het tegenovergestelde waar!

Als je de volgende code in GCC (14.2, X86-64) stopt:


    if( x & 1) {
        printf("1");
    } else {
        printf("0");
    }

krijg je totaal 43 byte objectcode

Maar de kortere versie:


    printf(x&1 ? "1": "0");

geeft 46 byte objectcode.

Als je naar de assembly kijkt snap je ook waarom:

GCC weet de simpele "printf("0") te vereenvoudigen tot een call naar putchar.
Maar de moeilijke printf(blah ? iets : iets) lukt hem dat niet.

Dus de compiler snapt leesbare code beter :-)

Voor AVR_GCC geld hetzelfde. Voor CLANG juist niet, die maakt van de kortere code ook een kortere object. Die overigens is beide gevallen groter is dan die van GCC. YMMV.

@arco: Maar als je deelt door 3 krijg je ook een ander resultaat. Deze twee voorbeelden geven exact hetzelfde resultaat...

Arco

Special Member

Zo geeft delen door 3 veel meer code als delen door 2... :+
(delen door een macht van 2 wordt door de compiler door een shift vervangen)

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Op donderdag 14 november 2024 15:41:20 schreef blurp:


    printf(x&1 ? "1": "0");

Dit geeft volgens mij op de nieuwste versies trouwens een waarschuwing. De compiler is niet zeker dat de eerste string naar printf geen van buiten af "beinvloedbare" dingen bevat.


    printf("%c", x&1 ? '1': '0');

is wat dat betreft beter en... dan gaat ie vast weer WEL putchar aanroepen....

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

Op donderdag 14 november 2024 16:40:58 schreef rew:
[...]Dit geeft volgens mij op de nieuwste versies trouwens een waarschuwing. De compiler is niet zeker dat de eerste string naar printf geen van buiten af "beinvloedbare" dingen bevat.

Ik krijg geen waarschuwing met gcc 17.2. Ik snap ook niet waarom de compiler zou kunnen denken dat een unnamed string constante beinvloedbaar zou zijn. Hij heeft geen naam, dus kan niemand ooit aan dat adres refereren.


    printf("%c", x&1 ? '1': '0');

is wat dat betreft beter en... dan gaat ie vast weer WEL putchar aanroepen....

Nee, want de compiler (iedere versie) gaat dan klagen dat printf een string wil, en geen los karakter. En dat is een error, geen warning.

Ik had hem verkeerd overgenomen. Inderdaad gaat ie zo wel putchar gebruiken, en is de resulterende objectcode wel korter(40 ipv 43 bytes)

Op donderdag 14 november 2024 16:52:58 schreef blurp:
[...]

Ik krijg geen waarschuwing met gcc 17.2. Ik snap ook niet waarom de compiler zou kunnen denken dat een unnamed string constante beinvloedbaar zou zijn. Hij heeft geen naam, dus kan niemand ooit aan dat adres refereren.

[...]

Nee, want de compiler (iedere versie) gaat dan klagen dat printf een string wil, en geen los karakter. En dat is een error, geen warning.

Lees hem nog eens? printf krijgt een string mee, namelijk "%c".
Toch, lijkt me dan

 putch(x&1 ? '1': '0');

beter.

Meep! Meep!
Hensz

Golden Member

Op donderdag 14 november 2024 15:01:25 schreef rew:
En ik verbaas me al jaren over mensen die denken dat als je het langer opschrijft het duidelijker wordt.

Ik denk dat je een goede kans hebt dat je niet de enige bent die jouw code moet begrijpen als is het maar dat je na een paar jaar (en soms veel sneller) je eigen code niet eens meer begrijpt. Je begrijpt mss wat iets doet, maar niet meer waarom dat ook al weer zo moest.

Op donderdag 14 november 2024 15:01:25 schreef rew:
Ik ben er op tegen om "teller" te schrijven. Iedereen weet dat de variabele i voor "actueel gebruik als teller in een loop" bedoeld is. Dan "teller" schrijven maakt de boel alleen maar onnodig langer en onoverzichtelijker.

Ik heb geen moeite met 'i', maar dan wel in loops die ik in één oogopslag kan zien. Bestrijken ze meer dan pakweg 10-15 regels, dan gebruik ik een langere naam om te weten waar deze var ook al weer voor diende. Misschien kun je het tijdens het schrijven nog wel allemaal in je hoofd houden, maar dat duurt ook maar een paar dagen. Blijkt er dan toch een bug ergens daar in de buurt te zitten, dan ben je veel langer aan het puzzelen. Met ook nog eens het risico dat je een nieuwe bug introduceert ipv plet.

Don't Panic!
Arco

Special Member

Daarom heb ik me aangeleerd om korte begrijpelijke Engelse namen te gebruiken...

In het verleden veel code gemaakt voor buitenlandse klanten, daarvoor is 'teller' net zo onduidelijk als 'i' (nog afgezien van dat een teller in bijv. het Duits een bord is... :) )
Engels worden alle programmeurs wel geacht te kunnen begrijpen.

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
JoWi

Golden Member

Op donderdag 14 november 2024 14:30:16 schreef Hensz:
[...]
Ik verbaas me al meer dan 40 jaar over al die mensen die denken dat als je minder tekst gebruikt voor je source-code je dan ook compactere object-code krijgt.

Helemaal niet, ik prefix variabelen vaak met een n,d of sz om aan te geven dat het een integer,double of string (zero terminated is).
Waar ik me over verbaas is dat bij elke update van de compiler de code weer een stuk groter en trager is. In de jaren 80 had je nog wat concurrentie (Borland/Lattice/Oasys/Microsoft/Zortech) in de compilers zodat bij een compiler update je programma in een keer een stuk kleiner en sneller was. Maar dat waren dan wel optimized compilers voor een specifieke architectuur (zoals voor de MC68020)

Ignorance is bliss
Hoeben

Golden Member

Als we dan toch met mooie code bezig zijn, uit mijn oude PLM51 tijd, wel even goed data declareren (waar in sommige talen dan ook veel kracht ligt:

DECLARE xbits STRUCTURE ((BO, B1, B2, B3, B4, BS, B6, B7) BIT);
DECLARE x BYTE AT (.xbits);

dan kan dit:

if x(teller) = 0
then ...
else...

Erg leesbaar.

Arco

Special Member

Hungarian notation is (nog steeds) erg handig... (prefix dw, sz, l, w,...)

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com