Printing a floating point number
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.
The next step was to rewrite the ETOFP routine using the new "multiply by 10" solution, and the calculatorcalls were replaced with direct ROMcalls. 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 ETOFP 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 ETOFP to a shorter (slower) one to fit my dirty favourite into the ROM. The results are convincing (see pict.2 SG81/E_BN)
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.
Both variants have same printout 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 FPTOBC routine.
If the number was less than zero (negative) but greater than 0.5, then it returned '0'
Solved  both ROMvariants contain the improved routine.
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.
The next step was to rewrite the ETOFP routine using the new "multiply by 10" solution, and the calculatorcalls were replaced with direct ROMcalls. 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 ETOFP 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 ETOFP to a shorter (slower) one to fit my dirty favourite into the ROM. The results are convincing (see pict.2 SG81/E_BN)
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.
Both variants have same printout 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 FPTOBC routine.
If the number was less than zero (negative) but greater than 0.5, then it returned '0'
Solved  both ROMvariants contain the improved routine.
 Attachments

 PRINT_FP.ZIP
 w. improved BNvariant
 (477.26 KiB) Downloaded 9 times
Last edited by zsolt on Mon Jul 03, 2017 4:33 pm, edited 1 time in total.
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
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
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/displayhandling during printing at the ZX81?
Are all solutions backwardcompatible? 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 ZX81ROM and E_BN: fantastic !
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/displayhandling during printing at the ZX81?
Are all solutions backwardcompatible? 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 ZX81ROM and E_BN: fantastic !
Last edited by siggi on Tue May 23, 2017 12:16 pm, edited 1 time in total.
My ZX81 webserver: online since 2007
http://zx81siggi.endoftheinternet.org/index.html
http://zx81siggi.endoftheinternet.org/index.html
Re: Printing a floating point number
I am glad, if you like it. The cores are in the attachment, feel free to use them.
Yes, the Speccy has a different algorithm, because of the different number storing  they handle wordsized 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
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
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
Re: Printing a floating point number
Hi,
It is very interesting, because the "printout section" prints only signifcant 8 digits.
Regards,
Zsolt
It is very interesting, because the "printout section" prints only signifcant 8 digits.
Let's see the code.
Regards,
Zsolt
Last edited by zsolt on Thu May 25, 2017 9:31 am, edited 1 time in total.
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
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
 (387 Bytes) Downloaded 12 times

 printFP1.asm
 (9.95 KiB) Downloaded 14 times
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
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
Re: Printing a floating point number
Many thanks to olofsen,
because he found a bug in the BNvariant (during his work on a H4THadaptation with floating point numbers),
which causes mistake, if you call the PRINTFP routine from a MCprogram 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 BNvariant in the attachment of the first post (there was a "ld c,5" instruction instead of a "ld bc,5").
Regards,
Zsolt
because he found a bug in the BNvariant (during his work on a H4THadaptation with floating point numbers),
which causes mistake, if you call the PRINTFP routine from a MCprogram 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 BNvariant in the attachment of the first post (there was a "ld c,5" instruction instead of a "ld bc,5").
Regards,
Zsolt
Who is online
Users browsing this forum: No registered users and 1 guest