How do I test for integers

Discussions about the BBC BASIC language, with particular reference to BB4W and BBCSDL
zeynel1
Posts: 21
Joined: Sun 15 May 2022, 11:35

How do I test for integers

Post by zeynel1 »

I could not find any information about this in the documents. How do I test a variable for its type?
Hated Moron

Re: How do I test for integers

Post by Hated Moron »

zeynel1 wrote: Fri 03 Jun 2022, 19:40 I could not find any information about this in the documents. How do I test a variable for its type?
To test the value of a numeric variable to see if it's an integer you can simply do:

Code: Select all

      IF  var = INT(var) THEN...
But I'm guessing that's not what you mean, because you refer to testing its type not its value. I'd be interested to know what the application is, because I don't recall ever wanting to do that, in 40+ years programming in BBC BASIC.

If that's really what you need, I think this code works:

Code: Select all

      IF !(^var + 8) AND &FFFF THEN PRINT "Floating-point" ELSE PRINT "Integer"
For example:

Code: Select all

      var = 123
      IF !(^var + 8) AND &FFFF THEN PRINT "Floating-point" ELSE PRINT "Integer"
      var = 123.0
      IF !(^var + 8) AND &FFFF THEN PRINT "Floating-point" ELSE PRINT "Integer"
nvingo
Posts: 41
Joined: Sat 28 May 2022, 22:40

Re: How do I test for integers

Post by nvingo »

So to clarify this for the layperson,
BBC Basic has a specific type for integer variables, with the % flag, 32-bit signed.
Is it the case that a normal numeric variable is stored in integer format until it is assigned a non-integer value?
So the code presented above inspects the variable's storage location to determine whether the format is floating-point or integer?
I realise there is a possibly significant processing (speed) as well as a numerical accuracy advantage in keeping numbers to integer whilst they can, has that been in BBC Basic since Acorn 1982 or introduced later?
As I recall, Sinclair Basic, which does not have a specific integer type, also does this for the same reasons.
There's another advantage in that the numeric variable type has a greater range than the integer type.
Would LET var=INT(var+.5) force it back to being stored as an integer?
OK I've played with a code snippet

Code: Select all

      MODE 7
      REPEAT
        INPUT TAB(0,23)"Submit number to test ";num$
        num=EVAL(num$)
        PRINT TAB(0,0)num;" is type ";FNisint(num);SPC(7)
      UNTIL num=0
      END


      DEF FNisint(var)
      IF !(^var + 8) AND &FFFF THEN ="Floating-point" ELSE ="Integer"
Entering 1 responds Integer
Entering 1.0 responds Floating-point
Entering num responds Floating-point
Entering INT(num+.5) responds Integer
Started using BASIC circa 1981 with CP/M, Video Genie, Sinclair ZX81, Acorn Atom, and progressed with ZX Spectrum, BBC Micro and Sinclair QL, Cambridge Z88, DOS, Windows. Wrote A-level project using school's BBC Micro with dual 800K floppy drive.
zeynel1
Posts: 21
Joined: Sun 15 May 2022, 11:35

Re: How do I test for integers

Post by zeynel1 »

[quote="Hated Moron" post_id=4087 time=1654290713 user_id=414]

Code: Select all

      IF  var = INT(var) THEN...
Thanks. It seems that this is what I wanted but I couldn't make it work.

(By the way, I just learned that in BASIC True==> -1 and False==>0)

So, I have this little code to convert minutes and seconds to seconds.

Code: Select all

      REM Convert minutes and seconds to seconds

      INPUT "Enter minutes:";Minutes%
      PRINT Minutes%=INT(Minutes%)
      IF Minutes=INT(Minutes) THEN
        INPUT "Enter seconds:";Seconds%
        PRINT Minutes%*60+Seconds%;" " "Seconds"
      ELSE
        PRINT "Enter an integer"
      ENDIF
      END
I want to test that the user enters an integer and not a string or a decimal number.

But, when I run the above program

Code: Select all

Enter minutes:? abc
        -1
Enter seconds:?
I enter a string "abc" but the test returns -1.

I guess I'm looking something like

Code: Select all

type()
in Python.

Thanks again for your help.
Hated Moron

Re: How do I test for integers

Post by Hated Moron »

zeynel1 wrote: Sat 04 Jun 2022, 06:20 I want to test that the user enters an integer and not a string or a decimal number.
The code you listed was:

Code: Select all

      INPUT "Enter minutes:";Minutes%
You are specifically placing the entered value into the integer numeric variable Minutes%, so of course it will always be a number (not a string) and it will always be an integer. Any information about what the user originally entered was completely lost in being transferred into the variable!

If you are concerned about whether it's an integer or a non-integer you must ensure that what he enters is placed in a regular (variant) numeric variable (e.g. Minutes); then you can perform the test. But that still won't tell you whether he entered something that isn't even numeric.

To perform that additional test you would have to place what he enters into a string variable and then do all the testing yourself in BASIC code. In its most simplistic form that could be something like:

Code: Select all

      INPUT "Enter minutes:";Minutes$
      IF STR$(VAL(Minutes$)) <> Minutes$ THEN PRINT "Not a number"
      IF INT(VAL(Minutes$)) <> VAL(Minutes$) THEN PRINT "Not an integer"
But this is prone to failure. For example suppose the user enters a trailing space after the number, that would cause the test to fail even though you might not care about that space. Or suppose the user enters 12.0 - that's a valid integer value but it will be rejected because of the decimal part.
Hated Moron

Re: How do I test for integers

Post by Hated Moron »

zeynel1 wrote: Sat 04 Jun 2022, 06:20 I guess I'm looking something like

Code: Select all

type()
in Python.
I don't know Python, but I should perhaps have added to my previous reply that BBC BASIC doesn't have a variable type which can contain either a numeric or a string (what I might call a 'true variant'). In principle it easily could, because internally it does indeed have such a data type - it's necessary to hold the value returned from a user-defined function (DEF FN) because that can be an integer, a floating-point number or a string:

Code: Select all

      DEF FNvariant
      CASE RND(3) OF
        WHEN 1: = 1234
        WHEN 2: = 56.78
        WHEN 3: = "string"
      ENDCASE
      = 0 : REM should never happen
So whilst BBC BASIC does internally use a 'true variant', that is not exposed to the programmer as a variable type.
nvingo
Posts: 41
Joined: Sat 28 May 2022, 22:40

Re: How do I test for integers

Post by nvingo »

zeynel1 wrote: Sat 04 Jun 2022, 06:20So, I have this little code to convert minutes and seconds to seconds.

Code: Select all

      REM Convert minutes and seconds to seconds

      INPUT "Enter minutes:";Minutes%
      PRINT Minutes%=INT(Minutes%)
      IF Minutes=INT(Minutes) THEN
        INPUT "Enter seconds:";Seconds%
        PRINT Minutes%*60+Seconds%;" " "Seconds"
      ELSE
        PRINT "Enter an integer"
      ENDIF
      END
I want to test that the user enters an integer and not a string or a decimal number.
The way I'd approach that, is to restrict the user to only entering the allowed characters.
Allow only digits, no alphabetic characters or punctuation, and the input will definitely be integer.
Something quickly like:

Code: Select all

Minutes%=VAL(FNinput("Enter minutes:","0123456789"))

DEFFNinput(prompt$,allowed$)
input$=""
PRINT prompt$;
REPEAT
key$=GET$
IF INSTR(allowed$,key$) input$=input$+key$: PRINT key$;
UNTIL ASC(key$)=13
=input$
(This would need adjustment to facilitate the user voiding, editing or deleting the input and tidy up the typing area).
I wouldn't be surprised to discover several suitable routines already written.

You could go further in this instance and only accept valid values between '00' and '59', ie. reject digits 6 and greater for the first digit and limit to two digits.
Hated Moron wrote: Sat 04 Jun 2022, 08:35internally it does indeed have such a data type - it's necessary to hold the value returned from a user-defined function (DEF FN) because that can be an integer, a floating-point number or a string:-
So whilst BBC BASIC does internally use a 'true variant', that is not exposed to the programmer as a variable type.

Code: Select all

Minutes%=FNinput("Enter minutes:","0123456789")
response$=FNinput("Yes/No:","NYny")

DEFFNinput(prompt$,allowed$)
input$=""
PRINT prompt$;
REPEAT
key$=GET$
IF INSTR(allowed$,key$) input$=input$+key$: PRINT key$;
UNTIL ASC(key$)=13
IF input$=STR$(VAL(input$)) = VAL(input$) ELSE =input$
Started using BASIC circa 1981 with CP/M, Video Genie, Sinclair ZX81, Acorn Atom, and progressed with ZX Spectrum, BBC Micro and Sinclair QL, Cambridge Z88, DOS, Windows. Wrote A-level project using school's BBC Micro with dual 800K floppy drive.
Hated Moron

Re: How do I test for integers

Post by Hated Moron »

nvingo wrote: Sat 04 Jun 2022, 11:32

Code: Select all

Minutes%=FNinput("Enter minutes:","0123456789")
response$=FNinput("Yes/No:","NYny")

DEFFNinput(prompt$,allowed$)
input$=""
PRINT prompt$;
REPEAT
key$=GET$
IF INSTR(allowed$,key$) input$=input$+key$: PRINT key$;
UNTIL ASC(key$)=13
IF input$=STR$(VAL(input$)) = VAL(input$) ELSE =input$
Here you have a function FNinput() which can return either a string or a number. On the first call of that function you assign the returned value to a numeric variable (Minutes%) so if the function happened to return a string an error would result. Similarly on the second call of the function you assign its value to a string variable, so if a number were to be returned an error would also result!

Exactly this happens in practice: if I respond to the first prompt by pressing just the Enter key (carriage return), i.e. not typing a number at all, an (empty) string is returned and a Type mismatch error results. That surely cannot be the intended behaviour?

A similar issue could arise if you added a decimal point (in order to allow entry of non-integer values) and/or a minus sign (to allow negative values) to the list of acceptable characters; then, too, the code could easily fail with an untrapped error.

I would most definitely not recommend that approach!
nvingo
Posts: 41
Joined: Sat 28 May 2022, 22:40

Re: How do I test for integers

Post by nvingo »

Hated Moron wrote: Sat 04 Jun 2022, 13:11Here you have a function FNinput() which can return either a string or a number. On the first call of that function you assign the returned value to a numeric variable (Minutes%) so if the function happened to return a string an error would result. Similarly on the second call of the function you assign its value to a string variable, so if a number were to be returned an error would also result!
You do realise that the whole point is to PREVENT a string from being typed in when a number is requested - it was only a quick modification of the previous code just to demonstrate that a function could return different types as per your own assertion I quoted - and yes I hadn't anticipated the case where a string was requested and digits were specified as valid characters which could lead to an evaluation of the string as numeric, but in the example posted, digits could not be entered for a yes/no response and it would not attempt to return a numeric value.
Hated Moron wrote: Sat 04 Jun 2022, 13:11if I respond to the first prompt by pressing just the Enter key (carriage return), i.e. not typing a number at all, an (empty) string is returned and a Type mismatch error results. That surely cannot be the intended behaviour?

A similar issue could arise if you added a decimal point (in order to allow entry of non-integer values) and/or a minus sign (to allow negative values) to the list of acceptable characters; then, too, the code could easily fail with an untrapped error.
It's easy enough to trap the specific cases where the input string is either null, '-' or '.'
I did state that the function offered needed some more work (delete/edit) and assuming the OP is writing a program to perform calculations on time measurements, would at least be capable of adding any necessary or ask here for further responses.

A more tailored way would be to present the input as list or scroll boxes like in the actual clock adjust dialogue:
23⇳ 59⇳ 59⇳ 99⇳ (mock-up).
edited
Started using BASIC circa 1981 with CP/M, Video Genie, Sinclair ZX81, Acorn Atom, and progressed with ZX Spectrum, BBC Micro and Sinclair QL, Cambridge Z88, DOS, Windows. Wrote A-level project using school's BBC Micro with dual 800K floppy drive.
Hated Moron

Re: How do I test for integers

Post by Hated Moron »

nvingo wrote: Sat 04 Jun 2022, 13:51 I did state that the function offered needed some more work (delete/edit)
Yes, but my point was that the design of the function made it inherently 'fragile', that is prone to failing unless all edge cases (at least one of which you admit you missed) are caught. For that reason it's the sort of code which, in my opinion, should be avoided in favour of an approach which is more fail-safe.

More specifically, I would suggest that a function which can return either a string or a numeric is never a sensible approach, unless the result is used in a context when both types can be accepted, which practically means only in a PRINT list.