Floating Point Numbers

Anything QL Software or Programming Related.
RWAP
RWAP Master
Posts: 2892
Joined: Sun Nov 28, 2010 4:51 pm
Location: Stone, United Kingdom
Contact:

Re: Floating Point Numbers

Post by RWAP »

You have to be careful when using the RANDOMISE command...

If you are using it in a program which runs as soon as the QL boots up, it will always be seeded with the same value (as the same amount of time will have passed since the QL was switched on!)...

RANDOMISE DATE would work a bit better if the QL is equipped with a battery backed clock...

The only real way of getting a truly random number without a battery backed clock, is for the program to include a prompt for the user to press a key before the RANDOMISE command, as the user will not always take the same amount of time to press the key or answer a question...


User avatar
polka
Trump Card
Posts: 207
Joined: Mon Mar 07, 2011 11:43 am

Re: Floating Point Numbers

Post by polka »

The only real way of getting a truly random number without a battery backed clock, is for the program to include a prompt for the user to press a key before the RANDOMISE command
And why not a prompt asking the user to enter the actual date ? Then you would have a "user backup'ed clock" QL at the same time. ;)

My own (paradoxical) way to use the RANDOMISE command was to give it always the same parameter value, in order to get always the same random number sequence :D :D :D

Paul


May the FORTH be with you !
POLKa
User avatar
tofro
Font of All Knowledge
Posts: 3091
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Floating Point Numbers

Post by tofro »

Well, RANDOMISE is probably the most mis-understood keyword in all BASICs.
Most people seem to think it has been introduced to allow you to make RND more random. But it's just the opposite - By default, RND is just about as random as it can be. Try starting a program containing RND (from the command line, not from BOOT) and cmpare the results. You'll get a different sequence of numbers every time.
for i = 1 to 10 : PRINT RND (1 TO 10)&" "
Will create a different sequence of random numbers every time.
RANDOMISE 100 : FOR i = 1 TO 10 : PRINT RND(1 TO 10)&" "
Will create the same sequence every time
RANDOMISE actually is there to make the "randomnesss" reproducable.
The easiest way to make RND really random would be
PRINT "Press any key to continue" : PAUSE -1 : RANDOMISE
This puts a bit of randomness into the seed, depending on how long the user waits to press a key (RANDOMISE uses some sort of "elapsed time since BOOT" as seed)
With RANDOMISE giving a reproducable sequence of "random" numbers for the same seed you can do interesting things, like for example cyphering a string with a known sequence and deciphering it only if the same seed is given for RANDOMISE again.

That's probably worth an article in one of the QL publications, though ;)


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
Mr_Navigator
QL Fanatic
Posts: 782
Joined: Mon Dec 13, 2010 11:17 pm
Location: UK, Essex
Contact:

Re: Floating Point Numbers

Post by Mr_Navigator »

Thanks for the replies, I was only using the RANDOMISE TIMER as an example, not that I wanted to use it. BTW on a PC using the RND function in QuickBASIC would always provide the same results each time you ran the program, the RND function wasn't that random and hence the need to use the RANDOMISE TIMER to ensure it was more random. In most computer systems the RND number generator was a fairly complex algorithm anyway so not truely random.

Memory jog - For those who remember the COMPUKIT UK 101 home built computer around the 1979, there was another similar computer system in kit form I seem to recall (but I could be wrong) that used the physical random noise generated by an open ended diode or transistor and used those signals for the RND generator, how cool was that [wakes up from a dream like state].

The reason for needed defined timing increments for myself were two fold, one for a visual aesthetic countdown clock, second and more importantly, having used a few older programs that worked fine on the original QL are difficult to use because of speed of emulators and different setups.

If I were to write a program that required actions to occur at a specific speed, not to fast not too slow, they would need appropriate timing attached to them in order to run more or less the same speed when used on diferent systems, hence the original question.


On a totally seperate note I found this website for the Compukit UK 101 http://home.micros.users.btopenworld.co ... uk101.html and also this for the ZX 80
http://home.micros.users.btopenworld.com/zx80/zx80.html, I still cannot remember what the other computer was though!


-----------------------------------------------------------------------------------
QLick here for the Back 2 the QL Blog http://backtotheql.blogspot.co.uk/
User avatar
tofro
Font of All Knowledge
Posts: 3091
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Floating Point Numbers

Post by tofro »

Hmmm.
The only function available from basic going beyond a resolution of 1s is: PAUSE
This allows your program to wait in 20ms increments. Unfortunately, the wait time is interrupted by keyboard activity, so not of much use for interactive games.
For everything else, it seems you need to live with the 1s ticks of DATE or need to revert to machine code or BASIC extensions. Simon Goodwins TIMER extensions mentioned by Dilwyn would be a good starting point. It looks as if the QL, S*BASIC and its QDOS were not designed for CPU clock independence and interactive gaming ;)

Cheers,
Tobias


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
polka
Trump Card
Posts: 207
Joined: Mon Mar 07, 2011 11:43 am

Re: Floating Point Numbers

Post by polka »

Here we have to my opinion, two subjects of confusion :

1/ how does the "original QL" floating point engine work ?
2/ what does the "original QL" RND function exactly do ?

2> It is said that without any parameter, the RND function returns an "unpredictable" floating point number in the range 0.0 to 1.0 . One could guess that in fact, it generates some kind of periodic sequence of (16bit ? 32bit ? ...?) integers that are then scaled down to this floating point range. What is the period of this sequence ? 2**16 ? 2**32 ? ...? signed ? unsigned ? the "seeding" by use of the RANDOMISE command should behave accordingly ! allowing either a 16bit or a 32bit or a ... parameter ? integer ? floating point ? in what range ? signed ? unsigned ?

1> I will try to probe into the QL floating point engine through my ComputerOne FORTH floating point extensions package, that apparently are plain entry points to this engine, AND because FORTH is intrinsically an integer virtual machine, letting see exactly how this d...d "floating point" is actually formatted. Should be specific... Sinclairwise ? :mrgreen:

Bye, Paul (now apparently chuggy microdrive : let's celebrate...)


May the FORTH be with you !
POLKa
User avatar
polka
Trump Card
Posts: 207
Joined: Mon Mar 07, 2011 11:43 am

Re: Floating Point Numbers

Post by polka »

I coded these few FORTH words to probe into the QL floating point format, and found interesting things...

Code: Select all

.( Probing into QL floating point format ) cr
: .. ( n<256 --- ) 0 <# # # #> TYPE ."  " ;
: ... ( n --- ) 256 /MOD ..  .. ;
: .W ( 16bit --- )   BASE @ >R HEX ... R> BASE ! ;
: .L ( 32bit --- )   BASE @ >R HEX ... ... R> BASE ! ;
: .F ( float --- )   BASE @ >R HEX ... ... ... R> BASE ! ;
: .Q FDUP .F ."      " F. ;
: *2 0 DO FDUP .Q CR F# 2 F* LOOP .Q CR ;
: /2 0 DO FDUP .Q CR F# 2 F/ LOOP .Q CR ;
Do not use the ancillary words .. and ...

The .W word expects a 16bit number (2 bytes) on the stack and displays it like 2 hex bytes. You use it like this (your input bold ! to signal to FORTH a 16bit integer, type simply the digits) :

1 .W
00 01 ok


The .L word expects a 32bit number (4 bytes) on the stack and displays it like 4 hex bytes. You use it like this (to signal to FORTH a 32bit integer, put a dot somewhere within the digits) :

1. .L
00 00 00 01 ok



The .F word expects a float number (6 bytes - QL float format) on the stack and displays it like 6 hex bytes. To input a float, just type F# in front of what you would input with SuperBasic :

F# 1.0 .F
08 01 40 00 00 00 ok


The .Q word expects also a float number on the stack but displays it both like 6 hex bytes and as a float (in the same format as with SuperBasic PRINT)

Code: Select all

F# 1.0 .Q
08 01 40 00 00 00      1 ok
The two following words *2 and /2 expect you to input first a float and then an integer before executing them and will output a table of ".Q results" :

Code: Select all

F# 1.0 32 *2
08 01 40 00 00 00      1 
08 02 40 00 00 00      2 
08 03 40 00 00 00      4 
08 04 40 00 00 00      8 
08 05 40 00 00 00      16 
08 06 40 00 00 00      32 
08 07 40 00 00 00      64 
08 08 40 00 00 00      128 
08 09 40 00 00 00      256 
08 0A 40 00 00 00      512 
08 0B 40 00 00 00      1024 
08 0C 40 00 00 00      2048 
08 0D 40 00 00 00      4096 
08 0E 40 00 00 00      8192 
08 0F 40 00 00 00      16384 
08 10 40 00 00 00      32768 
08 11 40 00 00 00      65536 
08 12 40 00 00 00      131072 
08 13 40 00 00 00      262144 
08 14 40 00 00 00      524288 
08 15 40 00 00 00      1.048576E6 
08 16 40 00 00 00      2.097152E6 
08 17 40 00 00 00      4.194304E6 
08 18 40 00 00 00      8.388608E6 
08 19 40 00 00 00      1.677722E7 
08 1A 40 00 00 00      3.355443E7 
08 1B 40 00 00 00      6.710886E7 
08 1C 40 00 00 00      1.342177E8 
08 1D 40 00 00 00      2.684355E8 
08 1E 40 00 00 00      5.368709E8 
08 1F 40 00 00 00      1.073742E9 
08 20 40 00 00 00      2.147484E9 
08 21 40 00 00 00      4.294967E9 
ok

F# 1.0 32 /2
08 01 40 00 00 00      1 
08 00 40 00 00 00      .5 
07 FF 40 00 00 00      .25 
07 FE 40 00 00 00      .125 
07 FD 40 00 00 00      6.25E-2 
07 FC 40 00 00 00      3.125E-2 
07 FB 40 00 00 00      1.5625E-2 
07 FA 40 00 00 00      7.8125E-3 
07 F9 40 00 00 00      3.90625E-3 
07 F8 40 00 00 00      1.953125E-3 
07 F7 40 00 00 00      9.765625E-4 
07 F6 40 00 00 00      4.882813E-4 
07 F5 40 00 00 00      2.441406E-4 
07 F4 40 00 00 00      1.220703E-4 
07 F3 40 00 00 00      6.103516E-5 
07 F2 40 00 00 00      3.051758E-5 
07 F1 40 00 00 00      1.525879E-5 
07 F0 40 00 00 00      7.629395E-6 
07 EF 40 00 00 00      3.814697E-6 
07 EE 40 00 00 00      1.907349E-6 
07 ED 40 00 00 00      9.536743E-7 
07 EC 40 00 00 00      4.768372E-7 
07 EB 40 00 00 00      2.384186E-7 
07 EA 40 00 00 00      1.192093E-7 
07 E9 40 00 00 00      5.960464E-8 
07 E8 40 00 00 00      2.980232E-8 
07 E7 40 00 00 00      1.490116E-8 
07 E6 40 00 00 00      7.450581E-9 
07 E5 40 00 00 00      3.72529E-9 
07 E4 40 00 00 00      1.862645E-9 
07 E3 40 00 00 00      9.313226E-10 
07 E2 40 00 00 00      4.656613E-10 
07 E1 40 00 00 00      2.328306E-10 
ok 
I wrote also two SuperBasic procedures to compare its printing of floats with the output of the FORTH Q. word (part of the floating point extensions like F# ).

Code: Select all

REM SuperBasic procedure doing the same thing than *2
100 DEFine PROCedure QLFM(FLA,K)
110 OPEN #5,flp2_bobo
120 FOR i = 1 TO K
130     PRINT#5,FLA
140     FLA=FLA*2
150 END FOR i
160 PRINT#5,FLA
170 CLOSE#5
180 END DEFine QLFM

Code: Select all

REM SuperBasic procedure doing the same thing than /2
100 DEFine PROCedure QLFD(FLA,K)
110 OPEN #5,flp2_bobo
120 FOR i = 1 TO K
130     PRINT#5,FLA
140     FLA=FLA/2
150 END FOR i
160 PRINT#5,FLA
170 CLOSE#5
180 END DEFine QLFD
The output is absolutely the same !

This means that the FORTH floating point extensions simply send what you typed after F# like a string to SuperBasic floating point conversion routine and get back 6 bytes following QL float format. And that the F. word simply sends 6 bytes to SuperBasic floating point print formatting routine and gets back a string to display (exactly the same as SuperBasic would print). And all the other floating point extensions words simply call Qdos floating point engine routines. No hassle, no fuss !... for the ComputerOne FORTH designers ;)

What playing with the .Q and the *2 and /2 is learning us about QL floating point internal format :

The first two bytes code the exponent, but only on their 12 L.S.bits

0800 means *2**0
0801 means *2**1
0802 means *2**2
etc.
07FF means /2**1
07FE means /2**2
etc.

The four following bytes are the mantissa with values going from (with exponent 0800) :

40000000 meaning +0.5
to
7FFFFFFF meaning +1.0 (almost)
and from
80000000 meaning -1.0
to
BFFFFFFF meaning -0.5 (almost)

mantissa Values under 40000000 or over BFFFFFFF are valid for the floating point engine, but are not generated by it.

Zero has this special format : 0000 (exp.) 00000000 (mant.)

Bye Paul


May the FORTH be with you !
POLKa
User avatar
Mr_Navigator
QL Fanatic
Posts: 782
Joined: Mon Dec 13, 2010 11:17 pm
Location: UK, Essex
Contact:

Re: Floating Point Numbers

Post by Mr_Navigator »

Ok, well thanks guys, I got a bit lost amongst the 1s and 0s, however I think I will have a play around with the TIMING command at the weekend and see what comes up.


Update:
BTW, this is what I was trying to emulate in the QL

http://www.online-stopwatch.com/full-screen-stopwatch/


-----------------------------------------------------------------------------------
QLick here for the Back 2 the QL Blog http://backtotheql.blogspot.co.uk/
User avatar
dilwyn
Mr QL
Posts: 3062
Joined: Wed Dec 01, 2010 10:39 pm

Re: Floating Point Numbers

Post by dilwyn »

Mr_Navigator wrote:Ok, well thanks guys, I got a bit lost amongst the 1s and 0s, however I think I will have a play around with the TIMING command at the weekend and see what comes up.


Update:
BTW, this is what I was trying to emulate in the QL

http://www.online-stopwatch.com/full-screen-stopwatch/
Ah, now I see what you mean.
If you want large text, I can send you a copy of my Enlarge routine for printing large characters on the screen.
There are several ways of approaching the "millisecond" counter. As the screen will only refresh up to 50 times per second, a 1/50 second counter for the fast units could be done with a routine like
FOR a = 1 TO 50 : AT y,x : PRINT a; : PAUSE 1

If you really wanted a very fast counter, you could use just a simple FOR...END FOR loop to print random numbers so fast you couldn't really see them. Or another possibility is that before your counter starts, you could probably do a speed test to see approximately how many FOR...END FOR iterations per second on the system the program is running on, e.g. this one counts how many in about 10 seconds:

time=date
FOR loop=1 TO (very large number)
AT y,x:PRINT 0; :REM for timing purposes, and use red on red or black on black to hide it
IF DATE > (time+9) THEN loopspersecond=loop : EXIT loop
END FOR loop

The variable loopspersecond now gives a rough idea of how many FOR/END FOR loops can be executed in a second and you can then divide it for an approximation of how many in whatever units you want. It will only ever be approximate (and very approximate at that) but for what you want to do it may be good enough, perhaps.

All the loop does is execute a continuous FOR loop printing (invisibly) a number to emulate what you'll be printing for timing purposes, and once ten seonds have passed it remembers how many loops it managed to do. Obviously, adjust to suit your program.

Dilwyn


User avatar
polka
Trump Card
Posts: 207
Joined: Mon Mar 07, 2011 11:43 am

Re: Floating Point Numbers

Post by polka »

Wouldn't this scheme only work if SuperBasic is the only job running ? Otherwise, I guess that other jobs may be stealing 1/50sec time slices ? same question for PAUSE 1 : normally it pauses the computer (actually the SuperBasic job) for at least one 1/50sec cycle, but actually may it not pause for longer ?

Would it be possible (to your opinion) to add a little counting code sequence linked directly into the Qdos scheduler, which for sure ticks every 1/50sec ? If so, I think it would be the best timer practical...

Paul


May the FORTH be with you !
POLKa
Post Reply