Cardinality vs. Ordinality

In his book Number, Midhat J. Gazalé talks about two primary components of number sense: cardinality and ordinality.[1] The former is about naming, telling things apart, without regard for any necessary ordering.

We use numbers for this purpose all the time, on license plates for example. You might guess a chronological order, but it's unimportant. The main thing is to have a "primary key" distinguishing each item in inventory uniquely -- a name, a code, a serial number.

Numbers used for their cardinality don't usually need to be operated upon e.g. by the addition or subtraction operators. You don't find yourself adding social security numbers together, or zip codes, which is why these digit sequences, along with phone numbers, are most often defined as "character strings" in a database -- they're like letters strung together. Indeed, when it comes to postal codes, many countries mix alpha and numeric markings in the same string.

Ordinality involes order. With alphabets, order starts to enter the picture. We know that A comes before B. But cardinality is still of primary importance, and whereas we may know the order for A-Z, most of us are not trained to think of how punctuation marks are ordered (e.g. does * come before or after %), nor do we necessarily have much ordinal sense vis-a-vis other systems of ideograms comprising human language -- even if we can still see there's a difference between any two chosen at random (cardinal sense).

Ordinality and cardinality blend together in various contexts to create different senses of numeracy. A good application to begin with in the classroom is unicode. Unicode is an international standard for mapping the symbols and glyphs used by many cultures to a single database of 1s and 0s, a binary inventory in which every important sign gets its uniquely identifying serial number. Unicode is a descendent of ASCII (American Standard Code for Information Interchange) but instead of just 8-bits per character (one byte), it allows 16 bits or even 32 bits.

For every new bit, you double the "primary key space". One bit names two possibilities: 1 and 0. Add another bit and you have four: 01 00 11 10. Add another bit and you have eight: 001 000 011 010 101 100 111 110. And so on. Just multiply 2 by itself as many times as there are bits and you'll know the total size of the potential binary inventory. 2^8 = 256 possible serial numbers of ASCII. 2^16 = 65,536 in 16-bit unicode, which is a big enough key space to handle not just A-Z, 0-9, but the symbols used by a large number of cultures, including Arabic, Sanskrit and Chinese.

When we identify a symbol by its 16-bit string of 1s and 0s, another naming convention comes into play. Since the early days of computers, people have broken bit strings into 4-bit groups (2^4=16) and identified these with the numbers 0-15 (in decimal, or base 10) or 0-F (in hexadecimal, or base 16). Here's what that looks like:

  Bits Dec Hex  Bits Dec Hex  Bits Dec Hex  Bits Dec Hex
  0000  0   0   0100  4   4   1000   8  8   1100  12  C
  0001  1   1   0101  5   5   1001   9  9   1101  13  D
  0010  2   2   0110  6   6   1010  10  A   1110  14  E
  0011  3   3   0111  7   7   1011  11  B   1111  15  F
So if you want to talk about the bit string:
you start by breaking that up into groups of 4 bits:
               1100 0010 1000 1011 
and then swap in the hexadecimal equivalents:
You've compressed 16 bits into 4 hexadecimal digits, each representing 4 bits.


  1. Go to the unicode charts web page at and open the geometric symbols chart. What is the hexadecimal code for the
       WHITE PARALLELOGRAM         ___________
       BLACK UP-POINTING TRIANGLE  ___________
       WHITE DIAMOND               ___________
  2. Boot Python.[2] The chr() built-in function will give you the ASCII character corresponding to a number between 0 and 255. What are the characters corresponding to the following decimal numbers:
       65 = _____  97 = _______ 68 = _________
  3. Write your name as a series of decimals. You can use the Python ord() function, which accepts a character and outputs the corresponding ASCII decimal.
  4. Write a short Python program that lists the decimals corresponding to every character in a string you provide. Hint: use ord(). e.g.
       >>> conv("1-2-3 testing")
       [49, 45, 50, 45, 51, 32, 116, 101, 115, 116, 105, 110, 103]
  5. Modify the program in 4. to list the characters using hexadecimal instead of decimal numbers. Hint: use hex() e.g.
       >>> conv("1-2-3 testing")
       ['0x31', '0x2d', '0x32', '0x2d', '0x33', '0x20', '0x74', 
        '0x65', '0x73', '0x74', '0x69', '0x6e', '0x67']
  6. What's 2^32? (Hint, use Python ** or pow(), w 2L Long)
1. 25B1 25B2 25C7
2. A a D
3. Example: "Kirby Urner" =
   75, 105, 114, 98, 121, 32, 85, 114, 110, 101, 114
4. One possible solution:
   >>> def decascii(s):
           return map(ord,s)
5. One possible solution:
   >>> def hexascii(s):
           return map(hex,map(ord,s))
6. >>> pow(2L,32)
[1] Midhat J. Gazale. 'Number'. 272 pages (February 22, 2000) 
  Princeton Univ Press; ISBN: 069100515X ; Dimensions (in inches): 
  1.08 x 9.48 x 6.41 

[2] Copyright (c) 2001 Python Software Foundation.  All Rights 
  Reserved. Freely downloable and royalty-free use:     

oregon.gif - 8.3 K
Oregon Curriculum Network