Zie Scherp Scherper - Hoofdstuk 4
Na dit hoofdstuk kan je:
Convert-bibliotheekSystem.Math en methoden in elkaar schuivendecimal nodig hebtRandomEen waarde van het ene type zomaar aan een ander type toekennen mag niet:
Je kan geen appelen in peren veranderen zonder magie. In C# heb je drie manieren:
Convert-bibliotheek van .NETstring om te zettenPlaats het doeltype tussen haakjes voor de waarde:
Belangrijk
Een variabele verandert nooit van type. Je zet enkel de waarde om en gebruikt die elders. kommagetal blijft een double.
Narrowing = een type omzetten met verlies aan data (bv. double naar int).
(int)20.9 geeft 20int gooit alles na de komma weg.tempGisteren en tempVandaag zijn int (20 en 25):
Dit geeft 22.0, niet 22.5. De expressie bevat enkel int’s, dus de deling is een gehele deling. Casten lost het op:
int, rechts rekent de hele expressie als double.Belangrijk
De plaats van je cast telt: (double)(a + b) / 2 geeft 22.5, maar (double)((a + b) / 2) geeft 22. Daar dwingen de haakjes de gehele deling eerst af.
Een klein type in een groter type past zonder dataverlies, dus geen cast nodig:
Casten mag hier wel ((double)hoofdMeting), maar het hoeft niet.
Parsing zet een string om naar een ander type:
De omgekeerde weg, naar tekst, doe je met .ToString() (dat elk type heeft):
Tip
Niet zeker dat de gebruiker een geldig getal invoert? Dan is int.TryParse() je vriend (zie appendix).
De gebruiker typt 3 en daarna 4. Wat toont dit?
34. Geen crash: de + plakt twee strings aan elkaar. C# heeft geen idee dat jij met "3" en "4" wou rekenen.
Waarschuwing
int getal = Console.ReadLine(); mag evenmin: de compiler weigert een string in een int te steken.
Uitlezen met ReadLine, bewaren in een string, en dan parsen naar het type dat je nodig hebt:
Wat de gebruiker typt komt binnen als string, Parse maakt er een double van.
Enkel de Parse-methode verandert mee met het type dat je nodig hebt:
| Gewenst type | Code |
|---|---|
int |
int.Parse(Console.ReadLine()) |
double |
double.Parse(Console.ReadLine()) |
bool |
bool.Parse(Console.ReadLine()) |
char |
char.Parse(Console.ReadLine()) |
string |
Console.ReadLine() (geen Parse nodig) |
Opmerking
De komende hoofdstukken mag je ervan uitgaan dat de gebruiker foutloze invoer geeft.
Steven moet de leeftijd van de gebruiker inlezen. De A.I. gaf hem dit en hij noemt het zelf “robuust”:
“Werkt perfect, ik heb het getest met 25”, zegt hij trots. Wat als de gebruiker vijfentwintig typt, of gewoon op enter duwt?
Opmerking
Dan crasht zijn programma. int.Parse verwacht tekst die echt een geheel getal voorstelt. Steven testte enkel de invoer die hij zelf in gedachten had, en de A.I. waarschuwde hem nergens. Foute invoer opvangen doe je met int.TryParse.
9.81 (onafhankelijk van je taalinstellingen)9,81, een Engels 9.81Waarschuwing
Het verkeerde scheidingsteken geeft geen foutmelding. Op een Belgische computer wordt 9.81 gelezen als 981, want de punt geldt daar als scheiding voor duizendtallen. Je programma rekent vrolijk verder met een gewicht van 981 kilogram.
Vreemde resultaten bij kommagetallen zonder dat er iets crasht? Kijk eerst welk teken je hebt getypt.
int is .ToInt32(), naar short is .ToInt16()Tip
Convert.ToBoolean geeft voor elk getal True, behalve voor 0 (of 0.0). En een bool naar een int omzetten lukt enkel via Convert, niet via casting.
Alle omzettingen samengevat.
Casting en parsing zijn compact en werken ook in andere talen. Beheers ze dus, ook al is Convert verleidelijk eenvoudig.
Verder o.a. Sin, Cos, Tan, Ceiling, Floor, Abs, Max, Min en Clamp. Voor Math schrijf je nooit new.
Waarschuwing
Sin, Cos en Tan rekenen in radialen. Math.Sin(90) geeft 0.8939... en niet 1. Reken eerst om: double hoek = graden * Math.PI / 180;
Tip
Typ Math. en IntelliSense toont alles. Cursor op een methode + F1 opent de documentatie.
Cannot implicitly convert type ‘double’ to ‘int’. Bijna alle rekenmethoden van Math geven een double terug, ook al stop je er gehele getallen in. Je lost het op zoals daarnet:
Tip
Uitzonderingen zijn Abs, Max, Min en Clamp: die geven terug wat je erin stopt. Math.Abs(-42) is dus een int.
Het resultaat van een methode mag de parameter van een andere methode zijn. Dat deed je al in double.Parse(Console.ReadLine()). De afstand tussen speler en monster, met Pythagoras:
C# werkt van binnen naar buiten: eerst de twee machten, dan de optelling, en pas dat totaal gaat in Sqrt.
Max en Min: de grootste of de kleinste van twee getallenClamp: begrenzen langs twee kanten tegelijk (waarde, ondergrens, bovengrens)Abs: het minteken verdwijnt, ideaal voor “hoe groot is het verschil?”Tip
Deze heb je in de praktijk vaker nodig dan alle sinussen samen.
NaN is besmettelijk: NaN + 5 blijft NaN, NaN * 1000 ook. Controleren doe je met double.IsNaN(resultaat), dat geeft je een bool.
Belangrijk
Oneindig en NaN laten je programma niet crashen. De foute berekening werkt stilletjes door, soms tot honderden regels verder waar je plots een onmogelijk resultaat ziet.
Elk datatype kent z’n eigen grenzen, en de compiler kent ze ook:
Bij kommagetallen bestaat zelfs double.PositiveInfinity en double.NegativeInfinity.
Waarschuwing
Overflow: de teller loopt over zoals de kilometerteller van een oude auto. Geen crash, geen waarschuwing. Verwacht je grote getallen, kies dan meteen een long.
Casting kapt af, ongeacht of er een 1 of een 9 na de komma staat. Bij negatieve getallen zie je het verschil met echt afronden:
Math.Truncate doet exact wat een cast doet, maar geeft je een double terug.
Floor en Ceiling gaan altijd dezelfde kant uit, een cast schuift altijd naar de nul toe.
47 studenten, 20 per bus:
Overal waar je iets moet vullen (bussen, dozen, pagina’s) mag je niets laten liggen, en daar is Math.Ceiling voor.
Waarschuwing
Vergeet je de cast naar double, dan deel je twee gehele getallen en krijg je 2 nog voor Ceiling iets kan doen. Er staan dan 7 studenten op de parking.
Math.Round rondt standaard af naar het dichtstbijzijnde even getal.
Rond je de reeks 0.5 tot 5.5 allemaal naar boven af, dan tel je er 3 bij. Met bankers rounding kom je exact op de echte som uit.
Waarschuwing
Handig voor bankiers, minder handig wanneer jouw quotering van 4.5 op 10 plots een 4 wordt.
Geef een MidpointRounding mee:
De volgorde van de parameters ligt vast: eerst het getal, dan het aantal cijfers na de komma, en pas als laatste de MidpointRounding.
Belangrijk
Convert.ToInt32 doet ook aan bankers rounding, en daar kan je het niet afzetten. Rond dan eerst zelf af: (int)Math.Round(4.5, MidpointRounding.AwayFromZero).
| Waarde | (int) |
Math.Round |
met AwayFromZero |
Convert.ToInt32 |
|---|---|---|---|---|
4.5 |
4 | 4 | 5 | 4 |
5.5 |
5 | 6 | 6 | 6 |
-2.5 |
-2 | -2 | -3 | -2 |
Belangrijk
Kies bewust. Wil je afkappen, gebruik een cast of Math.Truncate. Wil je afronden, gebruik Math.Round met MidpointRounding.AwayFromZero. Doe je niets, dan krijg je bankers rounding.
Bij F2 behoudt de variabele al haar cijfers, enkel de tekst op het scherm is ingekort. Bij Math.Round gooi je die cijfers echt weg.
Tip
Rond pas af op het laatste moment, vlak voor je het resultaat toont. Rond je te vroeg af, dan sleep je die fout doorheen je hele berekening mee.
4.35 * 100 levert 434.99999999999994 op en de cast kapt de rest af. Je klant is een cent kwijt, en bij duizend bestellingen merkt de boekhouding dat.
Tip
Voor alles wat op een kassaticket zou staan kies je decimal. Voor wetenschappelijke berekeningen, coördinaten en snelheden blijft double de juiste keuze.
Maak eenmalig een generator en vraag dan getallen op:
Belangrijk
De ondergrens telt mee, de bovengrens niet. Wil je een getal tot en met X, schrijf dan X + 1 als tweede parameter.
Tip
De new Random()-syntax pluizen we vanaf hoofdstuk 9 uit. Lig er nu nog niet van wakker.
Belangrijk
Maak daarom maar één generator aan. Wil je voor het testen telkens dezelfde reeks? Geef dan een vaste seed mee: new Random(666).
Code die compileert, is enkel grammaticaal correct. De betekenis kan nog altijd fout zijn:
Perfecte “code”, maar een logische fout (en een stevige explosie). Dat is een bug.
Voorspel telkens eerst wat er moet gebeuren: welke waarden, welke output? Klopt de werkelijkheid niet met je voorspelling, dan heb je waarschijnlijk een bug beet.
Waarschuwing
Zomaar door je code steppen en hopen dat de bug magisch verschijnt, werkt niet. Een programmeur die niet kan debuggen is als een vis die niet kan zwemmen.
Belangrijk
De verboden prompt: “Schrijf de C#-oplossing voor deze opgave.” Dat is als de Tour de France-winnaar de Mont Ventoux voor je laten opfietsen: je geraakt boven, maar leert niets.
Beter zijn prompts die je doen denken en waarmee je in dialoog gaat: laat AI code met ingebouwde fouten maken (zoek de fout), je oplossing met de modeloplossing vergelijken, of je ondervragen over de output van een stukje code.
Links doet de A.I. het denkwerk, rechts doe jij het.
Convert: ken het verschilReadLine geeft altijd een string: parse naar het juiste typeMath rekent met kommagetallen en in radialen; oneindig en NaN crashen nietdecimalRandom-generator volstaat; debuggen doe je door te voorspellenTip
Volgende halte: beslissingen nemen met if, vergelijkingen en switch.
Kennisclips
Dezelfde concepten uit dit hoofdstuk, maar dan in andere talen. Handig om later code in Python of JavaScript te leren lezen.
C# is streng. JavaScript zet types net automatisch om (coercion):
Die eerste regel doet C# ook: "5" + 3 levert daar eveneens de tekst "53" op. Het verschil zit in de tweede regel: "5" * 3 weigert de C#-compiler.
De naam van het type is er meteen de omzetfunctie:
Wiskunde zit in een aparte module die je eerst moet importeren:
C# heeft een aparte Parse per type, en Math hoort standaard bij de taal.
Deze Python-code rondt twee examencijfers af. Eén van de twee studenten gaat klagen. Waarom?
round() doet in Python ook aan bankers rounding: ligt een getal op de helft, dan gaat het naar het dichtstbijzijnde even getal. Wat Python niet heeft is de MidpointRounding-parameter.
Een handige samenvattende poster (Opgelet: gemaakt m.b.v. ChatGPT Image Gen 2).