Datatypen decimal är en kul sak som jag blev glad när jag hittade i C#. Den kan man, inte helt otippat, använda för att representera decimaltal. Alltså tal som har ett decimalkomma i sig. På så sätt liknar den datatyperna float och double som fungerar på samma sätt i C# som i Java.
Varför vill man då ha en ny datatyp när det redan finns två som borde göra jobbet? Det är vad jag tänkte försöka svara på i den här artikeln. Och jag tänkte att vi skulle börja med att bekanta oss med Börje Bankman som har tänkt starta en ny affärsbank. För att spara in på kostnaderna bestämmer han sig för att själv programmera ihop systemet som ska hålla ordning på kundernas konton. Han har just läst en nybörjarbok om C# så det borde inte bli några problem. Börje bestämmer sig för att använda variabler av datatypen double för att lagra kundernas kontosaldon. Då borde belopp kunna lagras med tillräckligt god precision. Hans första version av systemet innehåller följande klass:
class Sparkonto {
private double saldo;
public void Insättning(double belopp) {
saldo += belopp;
}
public void Uttag(double belopp) {
saldo -= belopp;
}
public void SkrivSaldo() {
Console.WriteLine("Saldot på kontot är: " + saldo);
}
}
För att testa att allt verkligen fungerar som det ska och att det nu går att spara öresbelopp på konton skriver Börje en testmetod som skapar ett konto och sedan sätter in 10 000 kronor i tioöringar. Så här ser testmetoden ut:
Testmetod() {
Sparkonto konto = new Sparkonto();
double tioöring = 0.1;
for(int i = 0 ; i < 100000 ; i++) {
konto.Insättning(tioöring);
}
konto.SkrivSaldo();
}
Till sin stora förvåning upptäcker Börje att testmetoden skriver ut att saldot är 10000,0000000188 efter insättningarna. Det blev visst lite mer än 10000. Det är ju inte så mycket pengar att det spelar så stor roll, men vem kan lita på en bank som inte kan summera tioöringar korrekt? Det verkar vara någon slags avrundningsfel som hade varit begripligt om man dividerat, men det enda som gjorts med saldot är ju enkel addition som inte borde kunna ge avrundningsfel. Börje sliter sitt hår och får till slut ringa sin vän Doris Datasnille som kan förklara vad som hänt.
Doris förklarar att datatypen double lagrar värden i det binära talsystemet och olyckligtvis finns det tal som inte kan representeras i det binära talsystemet utan att avrundas. Det här är inget som är unikt för just det binära talsystemet, det finns tal som inte kan lagras i vårt vanliga decimala talsystem med basen 10 heller. Ett enkelt exempel på det är en tredjedel, som måste avrundas till 0,33333 eller till något med ännu flera treor. Men hur många treor man än tar med så blir det alltid lite mindre än en tredjedel och om man väljer att avsluta med en fyra istället blir det lite mer. Det finns inget sätt att komma precis rätt. På samma sätt är det omöjligt att göra en exakt korrekt representation av en tiondel i det binära talsystemet. Det är just detta som Börje råkat ut för. Varje tioöring han sätter in på kontot är lite större än en tiondel.
Det är här datatypen decimal i C# kommer in som en trevlig sak. Den representerar tal i det decimala talsystemet, vilket gör att de oundvikliga avrundningsfelen som ibland sker blir på ställen där vi människor som lärt oss lite matematik i skolan är vana att se dem, som till exempel när vi ska representera en tredjedel eller för den delen pi.
När Börje hört detta ändrar han alla double till decimal i Sparkonto-klassen. Han måste också ändra testmetoden som efteråt ser ut så här:
Sparkonto konto = new Sparkonto();
decimal tioöring = 0.1M;
for(int i = 0 ; i < 100000 ; i++)
{
konto.Insättning(tioöring);
}
konto.SkrivSaldo();
Konstanten 0.1 måste skrivas med ett M i slutet för att tala om att det är det decimala talet 0,1 som avses till skillnad från det binärt reprecenterade double-talet 0,1. Nu blir summan tack och lov exakt 10000 kronor och Börje kan fortsätta med att programmera resten av systemet.
Java har inte någon primitiv datatyp som hanterar decimala tal. Istället kan man använda klassen BigDecimal och det är ganska jobbigt eftersom man inte kan använda den tillsammans med operatorer som + och -. Istället får man anropa metoder för att utföra beräkningar, vilket får till följd att programkoden blir ful och trist att läsa. Slutsats: datatypen decimal i C# är en bra sak.