Behandelde begrippen:
Een grootheid is iets waarvan we de waarde kunnen meten of tellen. Denk aan kilometers of bedragen in euro’s. Deze waarden zul je meestal omzetten naar integer
of float
.
Maar wat doe je met de grootheid ‘kleur’?
Als je de waarden van kleur omzet naar integer
kun je in je code onzinnige berekeningen doen: bijvoorbeeld blauw met 3 vermenigvuldigen.
Het is verstandig dit niet in je programma te doen, en tot op zekere hoogte kan de compiler je daarbij helpen. Het is belangrijk je bewust te zijn van de soorten schalen waarin grootheden kunnen worden uitgedrukt, en van de *relaties tussen verschillende grootheden (uitgedrukt in hun eenheden).
Er zijn verschillende soorten schalen die uitdrukken hoe waarden zich onderling verhouden.
Er zijn 4 bekende soorten schalen:
In deze volgorde nemen de relaties tussen waarden toe.
Het meest eenvoudige soort schaal is de nominale schaal. De waarden in een nominale schaal zijn verschillend van elkaar (anders waren het geen aparte waarden), maar dat is ook alles dat je kunt zeggen over die waarden. Voorbeelden: landen, kleuren.
Je kunt nominale waarden niet op een (zinvolle) *volgorde leggen. Denk bijvoorbeeld aan landen. of kleuren. Soms wordt aan een nominale opsomming om praktische redenen wel een volgorde toegekend, bijvoorbeeld om te kunnen sorteren en zoeken. Kleuren zou je alfabetisch kunnen sorteren.
Als de waarden van een schaal wel een duidelijke volgorde hebben dan spreken we van een ordinale (geordende) schaal.
Bij een interval schaal hebben de waarden uit de schaal niet alleen
Je kunt dus verschillen tussen waarden met elkaar vergelijken, en het gemiddelde bepalen. Bijvoorbeeld temperatuur, uitgedrukt in graden Celsius of Fahrenheit.
Bij een ratio schaal zitten de waarden op gelijke afstanden en er is een (zinnig) nulpunt. Dit betekent dat je waarden (zinnig) kunt vermenigvuldigen met een getal.
Ratio schalen zijn bijvoorbeeld de temperatuur in graden Kelvin, je leeftijd, of het saldo op je bankrekening. Strikt genomen: 10°C is 283°K. Kelvin is wel een ratio schaal, dus 2 keer 283°K is 566°K en dat is 293°C.
Een enumerate type (enumeratie, opsomming) is een C++ type dat een reeks waarden kan aannemen; elke waarde heeft een naam. Je kunt deze zelf definiëren.
De vier windrichtingen kun je definiëren in een enum type.
enum wind { north, east, south, west };
wind w = north;
Codevoorbeeld - enum van windrichting
Bij deze vorm van enumerate is de enumerate zelf
Dit betekent
enum wind { north, east, south, west };
wind x = 2; // geen foutmelding
x++; // geen foutmelding
x = north + east; // geen foutmelding
enum pole { north, south }; // error: duplicate enumerate value
Codevoorbeeld - Gebruik dubbele enum waarden (north en south) niet toegestaan
Enum waarden zijn globaal en moeten dus uniek zijn.
In C++ is er naast de bovenstaande C-style enum een nieuwe versie, de enum class. Iedere enum class is een apart type, los van integer, en de benoemde waarden zijn alleen zichtbaar ‘binnen’ de enum type naam. Hierdoor
In nieuwe code is het sterk aangeraden enum class te gebruiken in plaats van de gewone enum.
enum class wind { north, east, south, west };
wind x = 2; // error
x++; // error
x = wind::north + wind::east; // error
enum class pole { north, south }; // OK
pole y = pole::south;
Codevoorbeeld - Enum class
Enum class waarden zijn lokaal binnen de enum. Enumeraties zijn geschikt om nominale en ordinale schalen te implementeren, maar de waarden zijn geordend (je kunt ze vergelijken met <, >, <=, >=) dus voor een nominale schaal zijn ze eigenlijk minder handig. Je zou zelf een soort enumerate kunnen maken die als vergelijkingen alleen == en != ondersteunt, maar dat is veel werk en wordt dus niet vaak gedaan.
class language {
private:
int x;
constexpr language( int x ): x{ x }{};
static constexpr const char * const names[ 3 ]{
"dutch", "english", "french"
};
public:
constexpr static language dutch_() { return language{0}; }
constexpr static language english_(){ return language{1}; }
constexpr static language french_() { return language{2}; }
constexpr language( const language & rhs ): x{ rhs.x }{}
friend hwcpp::io::ostream & operator<<(
hwcpp::io::ostream & lhs,
language x );
bool operator==( const language &rhs ){
return x == rhs.x;
}
bool operator!=( const language &rhs ){
return *this != rhs;
}
};
constexpr const char * const language::names[ 3 ];
constexpr const language dutch { language::dutch_() };
constexpr const language english { language::english_() };
constexpr const language french { language::french_() };
Codevoorbeeld - Een DIY enum met alleen == en != (dus geen < etc.)
Voor interval en ratio schalen kun je gebruik maken van de diverse integer en float typen, afhankelijk van je gewenste bereik (hoogste en laagste waarde) en nauwkeurigheid. Wees je wel bewust van de beperkingen van deze typen.
Floating point typen (float en double) hebben ook een bereik, maar dat is zo groot (zeker voor double: 1.7E-308 to 1.7E+308) dat je daar meestal geen probleem mee zal hebben. Wat wel een probleem kan zijn is dat
==
en !=
niet zinnig om te gebruiken (we hebben ook geen =<
en >=
).Voor interval schalen moet je bedenken dat
*
en /
operatoren hebben. (En +
en –
zijn ook verdacht.)Als we een waarde meten dan is het resultaat niet alleen een getal. Is mijn lengte 185, 1.85, of 1.955466109E-16? (Om nog maar te zwijgen van de benadering 6”1). Zo’n getal heeft pas betekenis als je weet in welke eenheid (cm, m, C) het is uitgedrukt. Als je een kale (integer of floating point) variabele gebruikt om een dergelijk waarde op te slaan dan is het sterk aan te raden de eenheid in de naam van de variabele te verwerken, bijvoorbeeld length_cm.
In 1999 ging de Mars Climate Orbiter satelliet verloren doordat het onderdeel van besturingssoftware waarden doorgaf uitgedrukt in het Amerikaanse-Britse systeem (in pond-seconden), aan software die deze waarde interpreteerde volgens het SI-systeem (in Newton-seconden). Hierdoor remde de satelliet te weinig, kwam in een te lage baan om Mars terecht, en verbrandde. De atmosfeer van Mars is heel ijl maar toch genoeg om een dergelijke satelliet af te remmen.
Het is niet al te lastig om een eenvoudige templateklasse te maken waarin je waarden kunt uitdrukken ten opzichte van een gegeven eenheid, waarmee je eenheid problemen zoals die van de Mars Orbiter kan voorkomen.
Uitleg over templates, literals en user defined suffixes volgt later (deze stof maakt geen onderdeel uit van semester S2).