Douglas Crockford

Blog

Books

Videos

2024 Appearances

JavaScript

Misty

JSLint

JSON

Github

Electric Communities

Mastodon/Layer8

Flickr Photo Album

ResearchGate

LinkedIn

Pronouns: pe/per

About

Scaled Numbers

In How JavaScript Works, I demonstrated two ways to approximate real numbers using integers: Floating Point and Rationals. There is also a third way: scaled numbers. This was the very first way used on the first generation of computers. The technique is still effective today, especially on the cheapest microprocessors.

It is really simple.

scaled_number = int * scale_factor
int = scaled_number / scale_factor

In a scaled number, the range is traded off for a fractional piece, essentially

small_int + numerator / scale_factor

I first worked with these in 1983, writing a graphics library in which the pixel coordinates were real numbers. For example, the rect function took a scaled x, a scaled y, a scaled width, and a scaled height. That kind of stuff is pretty common now, but it was bleeding edge forty years ago.

Addition and subtraction of scaled numbers is really easy. If two numbers have the same scale factor, you can add and subtract them as if they are integers. Multiplication and division are a little bit hard. The result of the multiply instruction must be divided by the scale factor. This is because

(int * scale_factor) * (another_int * scale_factor)
    =
(int * another_int) * (scale_factor * scale_factor)

Similarly, when dividing, the dividend must first be multiplied by the scale factor. On the cheapest processors, make the scale factor a power of two, because shifting bits is a lot easier than multiplying and dividing.

Num9e6

My favorite scale factor is nine million. In a 64 bit system, this gives numbers as huge as

1024819115206 775807/9000000
1024819115206.0862007

and as tiny as

1/9000000
0.0000001

The overline above the seventh decimal place indicates that the digit is repeating.

0.000000111111...

An upper bound of a trillion is too small for some scientific applications, but is adequate for most business needs. That comes with six exact decimal places, which is something that IEEE 754 binary floating point can not do, and there is a repeating seventh digit. That makes it possible to exactly represent all of these fractions:

1/2, 1/3, 1/4, 1/5, 1/6, 1/8, 1/9, 1/10, 1/12, 1/15, 1/16, 1/18, 1/20, 1/24, 1/25, 1/30, 1/32, 1/36, 1/40, 1/45, 1/48, 1/50, 1/60, 1/72, 1/75, 1/80, 1/90, 1/96, 1/100, 1/120, 1/125, 1/144, 1/150, 1/160, 1/180, 1/200, 1/225, 1/240, 1/250, 1/288, 1/300, 1/360, 1/375, 1/400, 1/450, 1/480, 1/500, 1/600, 1/625, 1/720, 1/750, 1/800, 1/900, 1/1000, 1/1125, 1/1200, 1/1250, 1/1440, 1/1500, 1/1800, 1/1875, 1/2000, 1/2250, 1/2400, 1/2500, 1/3000, 1/3125, 1/3600, 1/3750, 1/4000, 1/4500, 1/5000, 1/5625, 1/6000, 1/6250, 1/7200, 1/7500, 1/9000, 1/9375, 1/10000, 1/11250, 1/12000, 1/12500, 1/15000, 1/18000, 1/18750, 1/20000, 1/22500, 1/25000, 1/28125, 1/30000, 1/36000, 1/37500, 1/45000, 1/50000, 1/56250, 1/60000, 1/75000, 1/90000, 1/100000, 1/112500, 1/150000, 1/180000, 1/225000, 1/300000, 1/450000, 1/900000

I call this system Num9e6, and it is much better suited to most computing tasks than the IEEE format we are currently stuck with.