23

Nowadays floating point is usually either 32 or 64 bits, sometimes 16, occasionally 128. But of course, the Basic interpreters on the 8-bit machines, having to implement floating point in software anyway, on CPUs that did things 8 or at most 16 bits at a time, were not obliged to stick to such power of 2 formats.

So I have never been particularly surprised that the old Commodore machines of my youth used 40-bit floating point. Nor would it surprise me to learn that other machines whose Basic was also licensed from Microsoft, such as the Apple II, did likewise. Why 40 bits? Well, everyone was still figuring things out as they went along. For all I know, maybe Bill Gates just stuck his finger into the air and picked a number at random. It would not be unreasonable, given the lack of hard data and the need to get things done in a hurry when he had told MITS they already had a Basic interpreter to sell.

But the BBC Micro and ZX Spectrum Basic interpreters were each independently written. And a quick Google search confirms they both used 40-bit floating point.

Huh? Why? It's not a power of 2, not a nice round number, doesn't match any particular requirement or standard that I know of, is neither the largest nor smallest reasonably possible value. What's going on? Did Microsoft choose a number at random and everyone else just copy them, or is there something else I am missing?

9
  • 13
    Re, "Nowadays, floating point is usually either 32 or 64 bits." More specifically, floating point is usually IEEE 754 floating point. Pior to the publication of that standard in 1985, it was the Wild West: Pretty much every system had its own internal representation for floating point numbers. I saw a book once that was basically a step-by-step instruction manual for how to construct your own floating point system. It was on the desk of my co-worker who was implementing the floating point arithmetic for a Lisp interpreter. Nov 13 '20 at 16:53
  • 8
    IIRC, 80bit floats are used in common FPUs Nov 13 '20 at 22:11
  • 5
    @HagenvonEitzen: The extended-precision type would have been nice, especially for systems without floating-point hardware, were it not sunk by some annoying design quirks of the 8087 and botched support by ANSI C.
    – supercat
    Nov 13 '20 at 23:40
  • 3
    40 is an even multiple of 8. Eight bits = 1 byte, so 40 bits = 5 bytes. Nov 15 '20 at 22:40
  • 5
    @BobJarvis-ReinstateMonica which is not bad on systems that don't have 16- or 32-bit word boundaries, but a multiple of 5 is still odd (both literally and figuratively).
    – RonJohn
    Nov 15 '20 at 23:59
42

The floating-point routines for Microsoft BASIC were written by Monte Davidoff in 1975, originally for the Altair, which used an Intel 8080 CPU. The source code had been lost for years, until Bill Gates’ former tutor discovered a copy in 2000 that had fallen behind his file cabinet two decades before.

Davidoff needed to invent his own floating-point format, and came up with: 8 exponent bits (bias-128), 1 sign bit, and 23 normalized mantissa bits. This was similar to DEC VAX single-precision floating-point numbers, but laid out in a more logical order.

In 1976, Gates, Allen and Davidoff wrote a 6502 version of their BASIC. When they were unable to fit it into 8K, they decided to put it in a larger ROM chip and add more features, including an “extended” 40-bit floating-point format. They chose to keep eight-bit exponents on the 8-bit CPU and extend the precision of the mantissa. Although Wozniak had already written Integer BASIC and was at that time working on a floating-point BASIC, he was also working on other projects at the same time. Steve Jobs felt it was taking too long and bought Microsoft’s instead. In Woz’s recollection:

My design style is to spend quite a bit of time thinking out every angle in my head and in rough sketches, and then to start coding. The first results aren’t visible right away, but at the end they come up very quickly. Steve Jobs got concerned that I wasn’t making enough progress. He even accused me of slacking and coming in at 10 AM in one staff meeting, but I pointed out that I’d been laying out our floppy PC Card [...] and that I’d been leaving at 4 AM every morning, long after even the Houston brothers, Dick and Cliff, had left.

Microsoft’s MBASIC for CP/M and its GWBASIC for MS-DOS were originally based on its 8080 BASIC for Altair, and used its 32-bit format at first, but went through several floating-point formats (including packed BCD in the Xenix version) before switching to IEEE format in GWBASIC 4.

When Sophie Wilson wrote the original BBC Micro BASIC for the 6502, and Richard T. Russell ported it to the Z80 in 1986, and later to several other machines (crediting Wilson as “the genius” behind the BBC Micro BASIC), they gave its “reals” the same range as Microsoft’s extended floating-point numbers. (Wilson’s previous BASIC, for the Acorn Atom, did not support floating-point.) In Russell’s words, “What we now know as BBC BASIC arose as the result of a compromise between what Acorn were already planning to produce and the BBC's desire for a ‘standard’ language. Programs written for Microsoft BASIC required little or no alteration to run on BBC BASIC, but programs written specifically for BBC BASIC could take advantage of its more sophisticated features.” BASIC VI for the ARM replaced the earlier number formats, which would have required unaligned memory access on a 32-bit RISC system, with 32-bit integers and 64-bit reals.

The Spectrum BASIC was an extension of the ZX81 BASIC by Steven Vickers, which was written at the same time as BBC BASIC. Vickers later said, “The only firm brief for the [ZX]81 was that the [ZX]80’s math package must be improved,” so it is likely that Sinclair wanted it to be able to match the floating-point precision of its competitors, such as the TRS-80 with its Microsoft BASIC. Several other British computers, including Sinclair’s notebooks in 1988, used a BASIC by Russell derived from BBC Basic.

8
  • 4
    The Spectrum ROM's FP software was an expansion of the ZX81's. The ZX81 was released in Mar '81 and the BBC Micro in Dec '81. So the Spectrum's FP format was not derived from the BBC BASIC FP format.
    – TonyM
    Nov 13 '20 at 13:50
  • 1
    @TonyM Thanks! Corrected.
    – Davislor
    Nov 13 '20 at 13:59
  • Thanks, upvoted after I corrected 'must' to 'may well' as it's not a known fact.
    – TonyM
    Nov 13 '20 at 14:18
  • 4
    Just one of Sophie Wilson's many achievements. "Genius" certainly is deserved. :)
    – Graham
    Nov 14 '20 at 12:11
  • 3
    Wozniak and Rankin's floating point routines (published in Dr Dobb's Journal, 1976) used a 4-byte format, but weren't used in any of Apple's products
    – scruss
    Nov 14 '20 at 15:11
23

Using a 32 bit signed mantissa and 8 bit unsigned exponent has one major advantage: You can re-use 32 bit integer math functions for operating on the mantissa.

That re-use saves memory. It may even be possible to optimize the 8 bit exponent maths if character maths are supported, as characters are typically stored as 8 bit unsigned ASCII.

The original Microsoft BASIC was intended to be 8k, but eventually was expanded to 16k due to lack of space. Before that decision was made it would have made sense to try to save as much space as possible, so it's easy to imagine how an 8/32 bit floating point format was chosen.

3
  • 2
    Such principles could make a floating-point format with 32-bit significand very useful on 32-bit platforms like the Cortex-M0 which lack floating-point units, but nothing in the 8-bit BASIC interpreters used 32-bit math for any purpose other floating-point significands.
    – supercat
    Nov 13 '20 at 16:22
  • 1
    I don't know about other implementations, but BBC basic certainly used 32-bit integers. Nov 16 '20 at 13:00
  • @PeterGreen Oh, what did it use them for?
    – rwallace
    Nov 29 '20 at 7:09
17

It's not a power of 2, not a nice round number

But it is :-) 1 byte exponent (with an assumed 1 bit always equal to one), 4 bytes mantissa, at least on the ZX Spectrum – see the ZX Spectrum manual. And since the mantissa and exponent are processed individually, the mantissa is a nice power of 2. Granted, this is less of an advantage without full 32 bit registers, but still.

3
  • One might reasonably expect that a float should have at least the same precision as an integer, so a 32-bit mantissa would be the minimum. For almost all real-world applications an 8-bit exponent is fine (I’m including sign bits).
    – Frog
    Apr 29 at 20:40
  • Furthermore, 32-bit integer routines can be used as part of floating-point arithmetic, which would save a valuable amount of ROM space.
    – Frog
    Apr 29 at 20:42
  • 1
    It's the mantissa that you can assume the high bit is always 1. You can't make that assumption on the exponent.
    – Hearth
    May 1 at 22:52
8

32-bit floating point has 23 bits of mantissa (8 used by exponent and 1 used by sign). This gives only 6 significant decimal digits of precision, possibly up to 9 but not with guaranteed precision. It is enough precision to claim you support floating point maths, but it isn't very much precision for some scientific needs.

I suspect they wanted to provide more significant decimal digits of precision but felt 64-bits was overkill, particularly on an 8-bit system. 40-bits gives 9 significant decimal digits of precision without taking too much more space.

According to Wikipedia, upon porting MS BASIC to the 6502, more than 8K of space was required. Now having 12K of space, there was room to expand to 40-bit FP.

Many earlier versions of Microsoft BASICs have only 32-bit FP, even the IBM PC ROM BASIC has 32-bit FP, but later MS BASICs typically have 40-bit FP. The MS BASIC on the Tandy 100/102 and MSX BASIC have 64-bit FP. Kyan Pascal for the C64 has 64-bit FP.

Atari BASIC has 48-bit FP but they are stored as BCD which causes some precision loss. Microsoft BASIC for the Atari has both 32-bit and 64-bit FP, allowing the programmer to choose between speed and precision as their needs require.

The PROMAL programming language for the C64, IBM PC and Apple II has 48-bit FP.

Nearly every 8-bit LOGO implementation has 32-bit FP, but the Atari version uses the 48-bit BCD FP routines in the OS ROM.

DEC's FOCAL-71 has 48 bit FP but as it is a 12-bit system, it is split into 36-bits for the mantissa and 12-bits for the exponent.

5
  • 2
    TI-99 used 8 byte decimal (not BCD) format as did Sharp pocket computer (BCD). Nov 30 '20 at 10:25
  • 1
    @PatrickSchlüter, interesting. Does the TMS9900 support decimal or was this done in software?
    – Tim Locke
    Nov 30 '20 at 18:16
  • 2
    For the Sharp pocket computer SC-61860 supported BCD operations and the format was straitforward BCD mantissa and exponent. On the TI it was different it used Radix-100 format. Each byte represented 2 digits but were encoded in normal binary, i.e. 99d would be represented as 63h TMS-9900 did not need BCD operation for that. unige.ch/medecine/nouspikel/ti99/reals.htm Dec 2 '20 at 12:03
  • 1
    Btw, SC-61860, the MPU in Sharp Pocket Computers also had 4 bit shift instruction, essential when you want to align the mantissas when adding. This mantissa aligning is what makes some implementations of floating points slower at adding than multiplying. Dec 2 '20 at 12:08
  • 3
    @PatrickSchlüter: IMHO, a major missed big-bang-for-the-buck optimization opportunity in MS BASIC would have been to say that if the exponent byte is zero, the bottom two bytes of the mantissa field should be interpreted as an integer value, rather than as being unconditionally zero (it would still be zero if the mantissa field is zero). Process or comparison of such values directly unless the result would exceed the range +/-65535, and otherwise convert to float and process as floating point.
    – supercat
    Apr 30 at 15:02
8

I used an IBM 360 a lot for numerical work around 1970, and found single-precision (32-bit) floating point almost, but not quite, adequate for a surprisingly wide range of problems; whereas "double precision" (64-bit) was overkill and slow. 40-bit floating point (32 bit mantissa) is probably an excellent compromise. But of course it depends on exactly what you are doing.

1
  • 1
    Yes, and wasting 24 bits per variable would have been a major inefficiency on a late-1970s small computer. A kilobit worth of RAM cost real money in these times :) Nov 16 '20 at 1:56
7

This all happened on an 8 bit computer. So the designer of the floating-point format was free to use 3, 4, 5, 6, 7, 8 or 9 bytes for floating-point numbers, there was no particular advantage of using 4 or 8 bytes.

The obvious difference is the precision, the storage requirements, the amount of code, and the execution time of operations. 32 bit float is Ok nowadays because we can switch to 64 bits if it is not precise enough, but at that point in time having two formats would have been too complicated. And 4 bytes for everything isn't good enough. 5 bytes for everything is borderline, just about the acceptable minimum.

Since BASIC had to fit into 8-16 Kbyte, code size was a problem. Up to some n, you can afford to do n operations with code replicated n times, but beyond that you'd need loops to keep the size small - and that really increases the execution time. So I think that was the reason to go with the minimum acceptable precision, and not use 6, 7 or more bytes.

2
  • 2
    That's a useful observation! There is no advantage to a power-of-two number of bytes (Having worked extensively with 8-bit CPUs a lifetime ago :) Nov 16 '20 at 10:37
  • Considerations of suitability for serious numerical work can hardly been a central in selecting a floating point format for BASIC.
    – vonbrand
    Feb 19 at 21:44
2

Floating point operations on most integer CPUs are actually performed on an unpacked representation, where the exponent is a freestanding value, the mantissa another such value, and the sign a separate boolean flag, sometimes a stolen bit from exponent or mantissa. So the use of packed IEEE representation is counter-productive since you have to keep unpacking the values before you operate on them, and then packing them again when done. Floating point BASICs also had an integer data type, and if the integers were 32-bit wide, then the subroutines used to operate on them were perfect for operations on the mantissa: you needed shifts and the 4 arithmetic operators: add, sub, mul, div - those were all available already on most 32-bit-integer BASICs. A 40-bit format is quite handy then: the only additional code that makes the floating point work is “wrapper” logic to orchestrate the mantissa operations, and do the simple 8-bit math on the exponent.

4
  • 2
    Floating-point BASICs often didn't have any facility for performing operations other than load/store on integer types. Even something like a%=b% would generally be processed by converting b% to a floating-point number, and then converting that floating-point value into an integer as part of the logic for storing it to an integer-type variable.
    – supercat
    Apr 29 at 19:21
  • 1
    @supercat True - some implementations shipped with a few dozen bytes unused at the end of the EPROM, so it was often that integer arithmetic was indeed a nicety that would be cut. And in any way, as long as you deal with integers, the floating point representation has no drawbacks other than slowing things down. I have been working on a disassembly of ABC80x BASIC by Luxor from Motala in Sweden, and it looks like they have a finely tuned integer type support that's fast enough to, say, scroll the text screen buffer via PEEK and POKE in loops. It almost didn't fit, it looks like. May 4 at 15:31
  • 1
    It's a shame there wasn't integer support at least for addition, subtraction, comparison, AND/OR/'NOT, and peek/poke addresses, since that would have made many kinds of programs more than twice as fast even if it required sacrificing other optimizations elsewhere. Or perhaps simply shortening some error messages.
    – supercat
    May 4 at 15:44
  • 1
    Code to perform addition, subtraction, and comparison would have been pretty small, and the other operations require having values in integer form anyway, so the only "extra" code that would be needed would be logic to allow a text-to-numeric conversion to start out using the logic employed by "goto" unless a decimal point is encountered or the number gets too big, and then fall back to floating-point format, code to convert floating point operands to FP format if they aren't already, and a small amount of code to handle the actual add, subtract, and compare operations with integers.
    – supercat
    May 4 at 18:36
2

The cheapest commonplace calculators in the 1970s could handle floating-point numbers 1.0 and larger with eight decimal digits of precision, and those smaller than 1.0 accurate to 0.0000001. A 32-bit floating-point format whose range extends through at least 1E20 will generally only be able to reliably process whole numbers in the range +/- 16,777,216, which is just over a sixth of the 99,999,999 that even the cheapest pocket calculators could handle. Pushing to a 40-bit format would extend that range to +/-4,294,967,296, which is about 42 times what a pocket calculator can handle. Even if one limits numerical output to nine significant figures, that's still superior to a cheap pocket calculator.

To be sure, even 40-bit math wouldn't have been a match for the more specialized calculators, some of which offered 12 or more digits of precision, but from a marketing standpoint it's a lot easier to say that a device which is specialized for some kinds of numerical precision can do them better than a general-purpose programming language, than to try to justify a $1,500 computer being unable to perform computations that could be handled by a pocket calculator that cost less than 1/100 as much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.