The ZX Spectrum has two formats for storing numbers, both 40 bits, or five bytes.
Jup, like many other machines - one for float and one for integer.
The second is some kind of 16-bit integer format,
Not some kind, but the integer format.
fluffed up to fit the same amount of space as a float.
Which was done to make variable memory management easy.
and a dummy byte which I believe is ignored.
But set to zero when an integer is stored
What is the motive for including this second format?
It's got several advantages
A one size fits all (numeric variables) approach, so memory management doesn't need to distinguish between integer and float
Integers are automatic detected, thus no need to declare integer variables like in other BASICs
Integers are always stored as integers
Calculation with integers can be sped up by avoiding conversion
Calculations with integers aren't harmed by being stored in float
[...] like a BASIC line number
I assume you mean for
GO TO or alike, as line numbers themselves are handled (and stored) differently.
or valid arguments to POKE or AT and so on, those can be assumed to be integers so that arithmetic can be done more quickly.
But then, the computer will have to check that the first two bytes equal 0xFF00 before proceeding to use the number.
They are a marker. Which speeds up execution a lot. If numbers in like a
POKE would be stored as float, then for each execution a float to integer conversion must be called (in fact twice; once for the byte to be poked as well). So instead conversion is only to be done if either marker byte is not present. A serious saving.
And that would incur a cost on any floating point calculation.
That's what? Two pairs of compare and jump, the second pair only executed at all for a tiny fraction of numbers? It's worth keeping in mind that a differentiation between FP or integer input from a variable has to be done anyway - so checking some flag stored at another position (where?) must happen - which makes the solution chosen almost free of cost in case of FP. Setup by a huge saving in not having to convert from FP if the number is to be used as integer anyway.
What if a mantissa happens to start with at least seven eight ones, and the sign happens to be negative, and the exponent happens to be zero? I see (the potential for) a bug there. That scenario would mean that the first two bytes of the float would be 0x00 and 0xff, so that could confuse the calculator routines.
No bug. It's a marker checked beforehand. Such a configuration is simply not an FP number. So Sinclair BASIC misses out of a tiny fraction of possible numbers compared to other BASICs with a similar encoding for FP. Not a big deal.
Especially not when it simplifies coding and speeds up execution as trade off.
P.S.: The whole details are covered in chapter 24 of the manual.
As there was a discussion about the impact of the introduction of this marker format on the range of FP numbers possible, be assured it's a rather small one.
The way it is implemented, it supports numbers from E-127 (Exponent encoded as 01h) to E+127 (FFh) (*1) without any restrictions. Without the integer marker it would go down to E-128. Sacrificing this for a clean integer handling in the same space and doing away with special integer variables is a cheap price to be payed.
Another idea that came up in discussion was the use of an appropriate exponent to simply use states where FP representation would match integer anyway. Like E+15, where the next two bytes would be exactly like the corresponding signed integer. Sounds cool at first, but it would end up in creating a mess.
For one, it only works with signed integer. Not a hurdle at first, as BASIC treats them that way anyway (*2), but latest when it comes to numbers smaller than +/-16384 representation would end up being not normalized - something that will screw floating point handling - unless additional checks are added, which in turn would slow down all FP calculation.
Next, and equally important, FP numbers in the range of integers that do have significant digits beyond the 15th (and thus being most definitive FP) would also identify as integer. To avoid this the trailing bytes (#4 and #5) must need to be checked for zero as well for safe identification as integer, making it 3 bytes to be tested instead of two, again slowing down identification.
Bottom line, the selected method is quite nice and solves its purpose at least as well as the more common MS-BASIC way of using variable identifiers (*3).
*1 - Exponents are encoded with high bit set for positive values to allow unsigned operations be used instead of signed. Or as the manuals put it " exponent +128 in the first byte".
*2 - Not sure ATM, but I think BASIC stores inline values as well in unsigned manner.
*3 - Depending on the way BASIC lines are stored, the the MS method of adding a type identifier (like
%) may result in an additional byte per occurrence within the program, making integer variables even more costly.