3.4 Kernel-Numbers

The top hierarchy Number class shows most of the behaviors inherited by the subclasses as Float, Integer and Fraction. The Smalltalk way to learn about a behavior is to point the System Browser toward a top hierarchy class and explore the method categories.

Let’s suppose we want to round a float number. In Number, we explore the Truncation and round off method category to discover several behaviors. The next thing to do is to test these messages in a Workspace to discover the one we are searching for:

1.264 roundTo: 0.1 ⇒ 1.3 
1.264 roundTo: 0.01 ⇒ 1.26 
1.264 roundUpTo: 0.01 ⇒ 1.27
1.264 roundTo: 0.001 ⇒ 1.264

Example 3.3: Rounding numbers, Workspace try out

Number is a very strange place to look for an indexed loop in a given interval. Nevertheless, an interval is defined by start and stop numbers. In the Number class, the method category intervals reveals related behaviors. These methods work polymorphically with most kinds of numbers:

1 to: 10 do: [:i | Transcript show: 1 / i; space]
⇒ 1 (1/2) (1/3) (1/4) (1/5) (1/6) (1/7) (1/8) (1/9) (1/10)

1 to: 10 by: 2 do: [:i | Transcript show: 1 / i; space]
⇒ 1 (1/3) (1/5) (1/7) (1/9)

1/10 to: 5/3 by: 1/2 do: [:i | Transcript show: i; space]
⇒ (1/10) (3/5) (11/10) (8/5) (1/10) (3/5) (11/10) (8/5) 

Float pi to: 5 by: 1/3 do: [:i | Transcript show: i; space]
⇒ 3.141592653589793 3.4749259869231266 3.80825932025646
4.141592653589793 4.474925986923126 4.808259320256459

Example 3.4: Interval loops (for-loop)

Now, in the Integer class, explore the method category enumerating, to find timesRepeat:. When a portion of code needs to be executed several times 15, without the need for an index, the #timesRepeat: message is sent to an integer. We already saw this variant in a previous section of this chapter. Throwing a 6-face die 5 times can be simulated with an integer:

5 timesRepeat: [Transcript show: 6 atRandom; space]
⇒ 1 2 4 6 2

Example 3.5: Throwing a die 5 times

Note: Expect a different result each time.

Intervals of numbers can be defined on their own, for future use:

1 to: 10
⇒ (1 to: 10)

1 to: 10 by: 2
⇒ (1 to: 9 by: 2)

Example 3.6: Interval

Intervals work with other kinds of objects such as Characters:

$d to: $h
⇒ #($d $e $f $g $h)

In fact, an interval is an object of its own. It is a sort of collection:

(1 to: 10) class
⇒ Interval

(1 to: 10 by: 2) squared
⇒ #(1 9 25 49 81)

(1 to: 10) atRandom
⇒ 4 "different result each time"

In Spacewar!, when a ship is destroyed it is teleported to a random position in the square gameplay area. Intervals are handy to pick random coordinates. In the example below, the variable randomCoordinate holds a block of code – called an anonymous function in other languages. It picks a random value in the interval consisting of the gameplay area’s left and right extents:

randomCoordinate := [(area left to: area right) atRandom].
aShip
   velocity: 0 @ 0;
   morphPosition: randomCoordinate value @ randomCoordinate value

Example 3.7: Teleport ship

 CuisLogo Compute the cosine values in the interval [0 ; 2PI], each 1/10. Output the result in the transcript.

Exercise 3.2: Cosine table

Integer numbers are represented in different bases when prefixed with the base and “r”. The r stands for radix, the base root by which the following number is interpreted. When executing and printing Ctrl-p on such a number, it is immediately printed in the decimal base:

2r1111 ⇒ 15
16rF ⇒ 15
8r17 ⇒ 15
20rF ⇒ 15
10r15 ⇒ 15

Example 3.8: Integer represented by different bases

Writing numbers as Mayans or Babylonians16:

"The Babylonians"
60r10 ⇒ 60
60r30 ⇒ 180
60r60 ⇒ 360
60r30 + 60r60 ⇒ 540 
(60r30 + 60r60) printStringRadix: 60 ⇒ '60r90' 

"The Mayans"
20r10 ⇒ 20
20r40 ⇒ 80 "pronounced 4-twenties in some languages"
20r100 ⇒ 400

Example 3.9: Counting like the ancients

Because of the nature of a number represented in base 2, shifting its bits left and right is equivalent to multiplying by 2 and dividing by 2:

(2r1111 << 1) printStringBase: 2 ⇒ '11110'
2r1111 << 1 ⇒ 30
(2r1111 >> 1) printStringBase: 2 ⇒ '111'
2r1111 >> 1 ⇒ 7

Example 3.10: Shifting bits

 CuisLogo How would you multiply the integer 360 by 1024, without using the multiplication operation?

Exercise 3.3: Multiply by 1024

Hiatus with decimal numbers

We saw decimal numbers are written with a dot “.” to separate the integer and the decimal parts: 1.5, 1235.021 or 0.5. The number 0.0000241 is more easily written with the scientific notation 2.41e-5; it means 2 preceded by 5 zeros or 2 as the fifth digit after the decimal dot.

 note Computers encode and store decimal numbers imprecisely. You need to be aware of that when doing computation and equality comparisons. Many systems hide these errors because they are very tiny errors. Cuis-Smalltalk does not hide this inaccuracy. There is good information about this in the class comment of Float.

0.1 + 0.2 - 0.3
⇒ 5.551115123125783e-17

Example 3.11: Computer dyscalculia!

In Example 3.11, the returned value should be zero but it is not the case. The computer returns 5.55e-17, or 0.0000000000000000555, it is very close to zero, but there is an error.

 CuisLogo Give 3 calculations showing errors compared to the expected results.

Exercise 3.4: Miscellaneous calculation errors with decimal numbers

When accuracy is mandatory use the Rational Numbers representation in Cuis-Smalltalk.

A rational number is written with the division symbol between two integers: do Ctrl-p on 5/25/2. Cuis-Smalltalk returns a rational number, it does not compute a decimal.

 CuisLogo What happens when executing this code 5/0?

Exercise 3.5: Toward the infinite

Let’s come back to our computer’s dyscalculia with decimal numbers. When using the rational numbers, the Example 3.11 becomes:

(1/10) + (2/10) - (3/10)
⇒ 0

Example 3.12: Calculation is correct using fractions!

This time we have the expected result. Under the covers, the computer only does the calculations with integer components so no roundoff results. This is a fine example where solving some problems requires a paradigm shift.

 CuisLogo Return to Exercise 3.4 and use rational numbers to represent decimal numbers. The errors will be resolved.

Exercise 3.6: Fix the errors

Cuis-Smalltalk knows how to convert a decimal number to a fraction, by sending the message #asFraction. We already acknowledged the computer’s dyscalculia with decimal numbers, this is why when asking for a fraction representation we have this strange answer. The internal computer representation of 1.3 is not exactly as it seems:

(13/10) asFloat
⇒ 1.3

(13/10) asFloat asFraction
⇒ 5854679515581645/45035996273704

Footnotes

(15)

More strictly, to be repeated an integer number of times.

(16)

Base 20 and 60 number representations are not exclusive to these civilisations, although they are the most documented use cases.