Douglas Crockford

Blog

Books

Videos

2025 Appearances

Slides

JavaScript

Misty

JSLint

JSON

Github

Electric Communities

Flickr Photo Album

LinkedIn

Mastodon/Layer8

ResearchGate

Pronouns: pe/per

Aptera

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. In an expression that both multiples and divides, the scaling operations can cancel out.

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.000000111111111111111111111111111111111111111111111111111111111111111...

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. It is much better suited to more computing tasks than the IEEE format we are currently stuck with, which offers these fractions:

1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/126, 1/256, 1/512, 1/1024, 1/2048, 1/4096, 1/8192, 1/16384, 1/32768, 1/65536, 1/131072, 1/262144, 1/524288, 1/1048576, 1/2097152, 1/4194304, 1/8388608, 1/16777216, ...

Which set of fractions looks to be more useful in your applications?

How JavaScript Works
Scaling Operator
DEC64