Talstelsels, bytegroottes en de eenheden waar mensen ruzie over maken

Hoe hex op bits afgebeeld wordt, waarom een schijf van 1 TB als 931 GB verschijnt, wat 0,1 plus 0,2 werkelijk oplevert, en hoe afronding, percentages en landgebonden opmaak een getal stilletjes veranderen.

Een opgeschreven getal draagt twee dingen: een waarde, en een reeks aannames over hoe het gelezen moet worden. 0xFF, 255, 0o377 en 11111111 zijn één hoeveelheid in vier notaties. Een terabyte en een tebibyte verschillen tien procent. 1,234 is in Londen ruwweg duizend en in Berlijn ruwweg één. De meeste numerieke bugs komen uit de aannames en niet uit de rekenkunde.

Binair, octaal, decimaal en hexadecimaal

Elk positioneel talstelsel werkt hetzelfde. Elke positie is het grondtal maal de positie rechts ervan waard, en de cijfers lopen van nul tot het grondtal min één. Grondtal 16 heeft zes extra cijfers nodig, en leent daarvoor A tot en met F voor tien tot vijftien.

Hexadecimaal wordt gebruikt voor bytes en kleuren omdat 16 gelijk is aan 2 tot de macht 4, dus één hexcijfer is precies vier bits en één byte precies twee hexcijfers, zonder overdracht ertussen.

0 0000   4 0100   8 1000   C 1100
1 0001   5 0101   9 1001   D 1101
2 0010   6 0110   A 1010   E 1110
3 0011   7 0111   B 1011   F 1111

Die tabel is de hele conversie. Van hex naar binair vervang je elk cijfer door zijn vier bits: 0x2F wordt 0010 1111. Terug groepeer je de bits vanaf rechts in viertallen, vul je links aan met nullen, en lees je elke groep uit de tabel af.

Voor decimaal gebruik je de positiewaarden. 0x2F is 2 x 16 + 15, oftewel 47. De andere kant op deel je herhaaldelijk door 16 en lees je de resten van onder naar boven:

200 / 16 = 12 remainder 8   -> 8
 12 / 16 =  0 remainder 12  -> C
200 decimal = 0xC8

Octaal propt drie bits in elk cijfer, en daarom worden Unix-rechten geschreven als 755: elk cijfer is één rwx-drietal, waarbij 7 gelijk is aan 111 en 5 aan 101. Een CSS-kleur als #FF8800 is drie bytes, één per kanaal, elk lopend van 0 tot 255, en de verkorte notatie #F80 van drie cijfers wordt uitgeschreven door elk cijfer te verdubbelen en niet door met nullen aan te vullen. De Number Base Converter dekt alle vier de talstelsels tegelijk.

Two's complement, en waar bereiken vandaan komen

Een byte bevat 256 verschillende patronen. Zonder teken gelezen zijn dat 0 tot 255. Met teken gelezen markeert de bovenste bit de negatieve helft, en een negatieve waarde is zijn patroon min 256. Die opzet heet two's complement, gekozen omdat gewone binaire optelling dan identiek werkt op waarden met en zonder teken.

Om een getal van teken te wisselen keer je elke bit om en tel je er één bij op. Het bereik is asymmetrisch omdat nul een van de 128 plekken in de niet-negatieve helft inneemt, dus een byte met teken loopt van -128 tot 127. Eén bij de bovenkant van het bereik optellen slaat om naar de onderkant:

  0111 1111    127
+ 0000 0001      1
= 1000 0000   -128

In de meeste talen wordt daar niets over gemeld; de waarde verschijnt eenvoudigweg aan de andere kant van het bereik. Dezelfde omslag bij 32 bits is het 2038-probleem, waarbij een Unix-tijdstempel van 32 bits met teken op 2.147.483.647 seconden op is. Zestien bits zonder teken geeft 0 tot 65.535, precies de reden waarom TCP-poortnummers daar ophouden, en waarom de Random Port Generator uit dat bereik trekt terwijl hij de gereserveerde poorten onder 1024 vermijdt.

Kilobytes, kibibytes en de ontbrekende gigabytes

Fabrikanten van opslag tellen in machten van tien. Besturingssystemen telden van oudsher in machten van twee en plakten het decimale voorvoegsel op het resultaat, en daar begint de discussie. De binaire voorvoegsels (KiB, MiB, GiB) bestaan om de dubbelzinnigheid weg te nemen.

Macht van tienDecimale waardeBinair voorvoegselBinaire waardeVerschil
kB, 10^31.000KiB, 2^101.0242,4%
MB, 10^61.000.000MiB, 2^201.048.5764,9%
GB, 10^91.000.000.000GiB, 2^301.073.741.8247,4%
TB, 10^121.000.000.000.000TiB, 2^401.099.511.627.77610,0%
PB, 10^1510^15PiB, 2^501.125.899.906.842.62412,6%

Een schijf van 1 TB bevat zoals verkocht 10^12 bytes. Deel dat door 2^30 en je krijgt 931,32, dus Windows meldt ongeveer 931 GB en gebruikt daarmee het decimale label voor een binaire hoeveelheid. Er ontbreekt niets; dezelfde bytes worden door een ander getal gedeeld. macOS en het meeste Linux-gereedschap melden inmiddels decimale GB, wat overeenkomt met de doos. Geheugengroottes zijn werkelijk binair, dus 16 GB RAM is echt 16 GiB.

Netwerksnelheden worden in bits geteld, niet in bytes, en altijd decimaal. Een lijn van 100 Mbps draagt 100.000.000 bits per seconde, oftewel 12,5 MB per seconde voor overhead. Protocoloverhead neemt een paar procent, dus 11 tot 12 MB per seconde is het realistische plafond. Een geadverteerd megabitgetal door acht delen is de snelle controle, en de Unit Converter dekt de opslageenheden.

Drijvende komma, en waarom geld geen float is

Binaire breuken kunnen alleen sommen van helften, kwarten, achtsten enzovoort weergeven. Een tiende heeft geen exacte binaire vorm, zoals een derde geen exacte decimale vorm heeft, dus 0.1 opgeslagen als IEEE 754 double is iets meer dan een tiende. Tel twee van zulke benaderingen op en de fout komt boven water:

0.1 + 0.2 = 0.30000000000000004

Een double heeft 53 bits significand. Daardoor is elk geheel getal tot 2^53 (9.007.199.254.740.992) exact weer te geven, samen met elke breuk waarvan de noemer een macht van twee is. Boven 2^53 wordt het gat tussen weer te geven waarden 2, dus 2^53 + 1 is gelijk aan 2^53. Daarom horen database-identifiers van 64 bits die via JSON reizen als strings te gaan: getallen in JavaScript zijn doubles, en een ID van 19 cijfers verliest stilzwijgend zijn laatste cijfers.

Sla geld op in gehele minieme eenheden (centen) of gebruik een decimaal type dat in grondtal tien werkt. Vergelijk floats nooit op gelijkheid; vergelijk het verschil met een kleine tolerantie. De Expression Calculator werkt ook in doubles, en erft dezelfde beperkingen.

Afronden, en twee keer afronden

Half up rondt een gelijkspel weg van nul af. Half even, ook bankiersafronding genoemd, stuurt een gelijkspel naar het dichtstbijzijnde even cijfer, wat de opwaartse vertekening wegneemt die half up over een lange kolom cijfers veroorzaakt. Het is de standaard van IEEE 754 en de regel in verschillende boekhoudstandaarden.

WaardeHalf upHalf even
0,510
1,522
2,532
3,544
-0,5-10

Rond één keer af, vanaf de oorspronkelijke waarde. 2,4449 op drie decimalen afronden geeft 2,445, en dat op twee afronden geeft 2,45, terwijl het origineel rechtstreeks op twee afronden 2,44 geeft. Doorgeschakelde afronding via tussenkolommen is een veelvoorkomende oorzaak van totalen die een cent verschillen. Een verwante verrassing: 2,675 rondt in de meeste talen af naar 2,67, omdat de opgeslagen double 2,67499999999999982 is.

Procent en procentpunt

Een percentage dat van 4% naar 5% gaat, is met één procentpunt gestegen, oftewel met 25% in relatieve termen. Beide zijn juist, en de eenheid moet erbij vermeld worden.

Procentuele veranderingen heffen elkaar niet op. Een daling van 50% gevolgd door een stijging van 50% laat 75 over, niet 100, omdat het tweede percentage van een kleinere basis wordt genomen. Een daling van p goedmaken vereist een stijging van p / (1 - p), dus een daling van 50% heeft 100% nodig, en een daling van 80% heeft 400% nodig. Opeenvolgende veranderingen vermenigvuldigen zich, waardoor een meetkundige reeks ontstaat in plaats van een som, en dat is de groei die de Number Sequence tool modelleert.

De verandering van een oude naar een nieuwe waarde is (new - old) / old x 100, en dat is wat de Percentage Calculator als verschil meldt. 20% btw eruit halen betekent delen door 1,2, niet 20% aftrekken. Percentages middelen over groepen van verschillende omvang geeft het verkeerde totaalcijfer, tenzij het gemiddelde gewogen is, en dat Sum and Average een mediaan naast het gemiddelde toont, maakt de scheefheid zichtbaar.

Getallen opgemaakt voor een landinstelling

Opmaak is een weergavekeuze, en hij is niet omkeerbaar zonder de landinstelling te kennen. 1.234 is in het Verenigd Koninkrijk iets meer dan één en in Duitsland duizend. 1,234 is precies andersom. Sommige landinstellingen groeperen met een spatie of een apostrof.

Landinstelling1234567.89
en-GB, en-US1,234,567.89
de-DE1.234.567,89
fr-FR1 234 567,89
de-CH1'234'567.89
en-IN12,34,567.89

De Indiase groepering zet het eerste scheidingsteken na drie cijfers en daarna om de twee cijfers, wat lakh en crore oplevert in plaats van duizenden en miljoenen. Een opgemaakte string terugparsen naar een getal is precies om deze reden lossy, dus sla waarden onopgemaakt op en verstuur ze zo, met een punt als decimaalteken, en maak ze pas op op het moment van tonen. Waar een getal voor een lezer eenduidig moet zijn, is het voluit schrijven met Number to Words de gebruikelijke aanpak op cheques en in contracten. Romeinse cijfers staan hier helemaal buiten: zonder positiewaarde en zonder nul werkt de Roman Numeral Converter met een streepjeslijst en niet met een talstelsel.