TI-S2

Units en waarden (title-id)

Inhoud

Waarden

Behandelde begrippen:

Grootheden

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).

Schalen: nominaal, ordinaal, interval en ratio

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.

Nominaal - geen volgorde

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.

Geen volgorde

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.

Ordinaal - geordend

Als de waarden van een schaal wel een duidelijke volgorde hebben dan spreken we van een ordinale (geordende) schaal.

Interval - geordend en vergelijkbaar

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.

Ratio - volgorde, afstand én nulpunt

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.

C++ Enum type

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.

Voorbeeld: windrichting

De vier windrichtingen kun je definiëren in een enum type.

enum wind { north, east, south, west };
wind w = north;

Codevoorbeeld - enum van windrichting

Een gewone enum

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 - globaal dus uniek

Enum waarden zijn globaal en moeten dus uniek zijn.

Enum class

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.)

Integers en floats

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

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

Interval schalen

Voor interval schalen moet je bedenken dat

Eenheden

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.

Mars Climate Orbiter ongeluk

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.

Buiten stof S2: templates, literals en user defined suffixes

Uitleg over templates, literals en user defined suffixes volgt later (deze stof maakt geen onderdeel uit van semester S2).