## Printing a floating point number

Anything Sinclair ZX Basic related; history, development, tips - differences between BASIC on the ZX80 and ZX81
zsolt
Posts: 164
Joined: Wed Apr 20, 2011 11:43 am
Location: Fót, Hungary

### Printing a floating point number

Dear Zeddy Fans,

Since debut of the last ROM improvements I dealt with the number printing on ZX81. I had several ideas and now I introduce 2 usable of them: a traditional and a dirty solution (the latter is my favourite).

The ZX81 BASIC Programming manual (chapter 27) "describes" the storage of the ZX81 number format only in few words: 5 bytes...

byte 1: the exponent - its value is 128 more than the real value (+128 offset)
(1 means 2^-127, 128 means 2^0 and 255 means 2^+127)
if its value is zero, then the whole number is zero (the next 4 bytes are also 0)

byte 2: the MSB of the mantissa - bit 7 is the signum bit
..
byte 5: the LSB of the mantissa.

The number is in normalized form (it is normalized to zero: zero is before the "binary point"), 0.5 =< mantissa value <1 -> the most significant bit is always 1, so it is not stored, this bit holds the sign of the number (0/1 -> +/-).

Let's begin with my favourite, the dirty solution:

The idea was to divide the full scale to 16 ranges, and store 16 base numbers in 10 digit BCD form, which represents the value of the least significant bit of the first number in the range. The ranges (and the BCD numbers also) are selected by the upper 4 bits of the binary exponent: \$0x - \$Fx.

At the start of the conversion the printable digits are '\$00,\$00,\$00,\$00,\$00' (10 digits in 5 bytes). The chosen base number is the first value for the factor wich represents the value of a binary mantissa bit. If the mantissa bit is set then this factor will be added to the BCD number. Then the factor will be doubled to represent the value of the next mantissa bit, and so on... (similar to manual counting)

For example the mantissa is 170 (b10101010). The factor (the base number) is 1000000000 E0.

bin. man. the factor the value

bit0 = 0 1.000000000 E0 0.000000000 E0
bit1 = 1 2.000000000 E0 2.000000000 E0
bit2 = 0 4.000000000 E0 2.000000000 E0
bit3 = 1 8.000000000 E0 1.000000000 E1
bit4 = 0 1.600000000 E1 1.000000000 E1
bit5 = 1 3.200000000 E1 4.200000000 E1
bit6 = 0 6.400000000 E1 4.200000000 E1
bit7 = 1 1.280000000 E2 1.700000000 E2

This algorithm is very fast (see BNR16 on pict.1) but requires much more bytes of the code space than available. So I made 2 shorter variants also, using only one (BNR1) and two (BNR2) base numbers. Both are much faster than the original ZX81 solution but still not the real thing - the 'BNR2' was not short enough, and the 'BNR1' was not fast enough in the range of everyday use.

The traditional solution is based on conversion to scientific form (i.e. 12345 -> 1.2345 e4). The integer part of the normalized number gives the first printable digit, and the rest come from the multiplication (by ten) of the fractional part: 'MT10x'. I was not happy with the results. The next idea was to replace multiplication by addition: 10x = 2x + 8x, where the "multiply by 2" is a simple "increment by 1" of the binary exponent! This little "trick" made it much faster, but was not fast and short enough.

picture 1 (All tests were performed on EO v1.2)
TestsOfTheAlgorithms.png (51.83 KiB) Viewed 986 times

The next step was to rewrite the E-TO-FP routine using the new "multiply by 10" solution, and the calculator-calls were replaced with direct ROM-calls. These modifications made the algorithm much faster and it can fit into the ROM!!! (see pict.2 SG81/E_MT)

The "Base Number" solution (I did not want to let go) does not use the E-TO-FP routine. What uses this subroutine at runtime? It is solely the "VAL" function, if you enter text that represents a number in scientific format. This is not too common - I belive - so I changed the E-TO-FP to a shorter (slower) one to fit my dirty favourite into the ROM. The results are convincing (see pict.2 SG81/E_BN)

picture 2 (All tests were performed on EO v1.2)
TestsOfTheNewROMs.png (73.98 KiB) Viewed 986 times

I ran another comparison in the 0..65535 (word sized integer) range, where the Speccy/SPONZY solution is very strong because of its "2 in 1" number format (see CHAPTER 24 of the manual)
The SG81/E_BN is fast enough in this case too.

picture 3 (All tests were performed on EO v1.2)
IntegerLoops.png (16.78 KiB) Viewed 986 times

Both variants have same print-out section, which is different (in 2 points) than the original:

- the first printed character is always a number (i.e. you see 0.01234 instead of .01234)
- if the number is less than 0.0001 or greater than 99999999, then it will show in normalized (scientific) format.

If you like any of these number printing solutions, then pls vote: Which should be the next improved ROM (SG81_E.ROM)

Thanks,
Zsolt

ps: During testing of the 'MT' solution I found a little bug in the FP-TO-BC routine.
If the number was less than zero (negative) but greater than -0.5, then it returned '-0'
Solved - both ROM-variants contain the improved routine.
Attachments
PRINT_FP.ZIP
w. improved BN-variant
Last edited by zsolt on Mon Jul 03, 2017 5:33 pm, edited 1 time in total.
ZX81 (8K), ENTERPRISE 128, [ZX SPECTRUM (48K,+,+128K,+2,+2A), TS1000, TS1500, TS2068, Cambridge Z88, PRIMO A64 (red)]

Bean
Posts: 26
Joined: Fri Aug 01, 2014 8:40 pm

### Re: Printing a floating point number

I am so glad that someone is working on this...Printing of values it terribly slow on the ZX81, and it makes the whole computer seem much slower than it really is.

I am actually working right now on a program that needs to print floating point values quickly.

It is a program that makes the screen behave like most other computers... That is it uses all 24 lines and then automatically scrolls.
It will also have some other features like not compressing the screen when scrolling (or if the SCROLL command it used).
Printing CHR\$(122) will set an INVERSE flag, and everything after it will be XOR'd with 128. Printing CHR\$(123) clears the INVERSE flag.

The program has it's own command execution loop, so you only do a RAND USR 16514 at the start, then you can just use normal PRINT commands without anything special needed.

I look forward to looking at the code. I'll let you know my thought...

Bean

siggi
Posts: 806
Joined: Thu May 08, 2008 9:30 am
Location: Dauernheim, Germany
Contact:

### Re: Printing a floating point number

Hi Zsolt
I would also prefer the "SG81/E_BN": it is the fastest solution in nearly all cases.
But why is the speccy still faster? Does it have other algorithms than the Zeddy? Or is it only caused by NMI/display-handling during printing at the ZX81?

Are all solutions backward-compatible? Some programs (Pokemon's IDE and ZXTEXT2P) generate floating point numbers by themselves. So the format of FP numbers must be the same.
And some programs (e. g. MEMOCALC) directly call the ZX81 calculator for their own calulations. Thus this interface must be the same.

Regards
Siggi

Edit: I tested printing with ZX81-ROM and E_BN: fantastic !
Last edited by siggi on Tue May 23, 2017 1:16 pm, edited 1 time in total.
My ZX81 web-server: online since 2007
http://zx81-siggi.endoftheinternet.org/index.html

zsolt
Posts: 164
Joined: Wed Apr 20, 2011 11:43 am
Location: Fót, Hungary

### Re: Printing a floating point number

Bean wrote:
Tue May 23, 2017 2:33 am
I am so glad that someone is working on this...
I am actually working right now on a program that needs to print floating point values quickly.
I am glad, if you like it. The cores are in the attachment, feel free to use them.
siggi wrote:
Tue May 23, 2017 9:29 am
But why is the speccy still faster? Does it have other algorithms than the Zeddy? Or is it only caused by NMI/display-handling during printing at the ZX81?
Yes, the Speccy has a different algorithm, because of the different number storing - they handle word-sized integer numbers also (more exactly -65535..+65535) - and probably the hardware displaying means a lot also.

The calculator has not been affected YET , that's my next target - the 4 basic operations.

Regards,
Zsolt
ZX81 (8K), ENTERPRISE 128, [ZX SPECTRUM (48K,+,+128K,+2,+2A), TS1000, TS1500, TS2068, Cambridge Z88, PRIMO A64 (red)]

Bean
Posts: 26
Joined: Fri Aug 01, 2014 8:40 pm

### Re: Printing a floating point number

I'm having a problem with my code. It is printing the fractional part TOO perfectly.

For example: 999.9 gives 999.9
But 9999.9 gives 9999.900002
and 99999.9 gives 99999.899994
and 999999.9 gives 999999.899903

I think the value I give is the actual value that is stored in the 5 bytes, but that is not what is wanted.

I'm not sure how to go about "fixing" it....

I'll keep working on it, but if anyone has any ideas I'd love to hear them.

Bean

zsolt
Posts: 164
Joined: Wed Apr 20, 2011 11:43 am
Location: Fót, Hungary

### Re: Printing a floating point number

Hi,

It is very interesting, because the "print-out section" prints only signifcant 8 digits.
Bean wrote:
Wed May 24, 2017 7:08 pm
I'm not sure how to go about "fixing" it....
I'll keep working on it, but if anyone has any ideas I'd love to hear them.
Let's see the code.
Regards,
Zsolt
Last edited by zsolt on Thu May 25, 2017 10:31 am, edited 1 time in total.
ZX81 (8K), ENTERPRISE 128, [ZX SPECTRUM (48K,+,+128K,+2,+2A), TS1000, TS1500, TS2068, Cambridge Z88, PRIMO A64 (red)]

Bean
Posts: 26
Joined: Fri Aug 01, 2014 8:40 pm

### Re: Printing a floating point number

Here is the code.

Use it like this:

0 REM machine code
10 FOR A=0 TO 10 STEP 0.1
20 PRINT USR 16514;A
25 PRINT
30 NEXT A

When it prints a value it does not move the print position to the next line, even if you don't have a ";" at the end, hence the need for the seperate PRINT line.

The value ("A" in the example above) can be any numeric expression.

The goal to to make the printing of values as fast as possible.

E notation is not supported. Values from +/- 9,999,999 are supported only.

In my final code(where you don't need USR on each print line), if you have a value that exceeds these limits you can still print it using STR\$ value

Any and all comments are welcome.

Bean
Attachments
printfp1.p
printFP1.asm

zsolt
Posts: 164
Joined: Wed Apr 20, 2011 11:43 am
Location: Fót, Hungary

### Re: Printing a floating point number

Oops, it is a completely other, very interesting solution: it prints out the numbers on the fly!

I encountered similar problems during program development (rounding), that's why the original (and my solutions too) use a buffer before printing.
You can round it only if you use a buffer, otherwise not.
Zsolt
ZX81 (8K), ENTERPRISE 128, [ZX SPECTRUM (48K,+,+128K,+2,+2A), TS1000, TS1500, TS2068, Cambridge Z88, PRIMO A64 (red)]

zsolt
Posts: 164
Joined: Wed Apr 20, 2011 11:43 am
Location: Fót, Hungary

### Re: Printing a floating point number

Many thanks to olofsen,

because he found a bug in the BN-variant (during his work on a H4TH-adaptation with floating point numbers),
which causes mistake, if you call the PRINT-FP routine from a MC-program and the B register is nonzero.
I completely forgot Erik's notification and now, when I tested a new addition routine, I experienced it

You can find the improved BN-variant in the attachment of the first post (there was a "ld c,5" instruction instead of a "ld bc,5").

Regards,
Zsolt
ZX81 (8K), ENTERPRISE 128, [ZX SPECTRUM (48K,+,+128K,+2,+2A), TS1000, TS1500, TS2068, Cambridge Z88, PRIMO A64 (red)]

zsolt
Posts: 164
Joined: Wed Apr 20, 2011 11:43 am
Location: Fót, Hungary

### Re: Printing a floating point number

Hi,

I ran some tests to see the printing characteristics of the new ROMs with renewed arithmetics.

The MT version seems now better:
(All tests were performed on EO v1.2)

But in the 0..65535 (word sized integer) range the BN is faster yet:
(All tests were performed on EO v1.2)
Regards,
Zsolt
Attachments
SG81F_MT.zip