Alyce Alyce Jan 23, 2012 - "Edited for page formatting and grammar."

Numbers in LB/JB Liberty BASIC

(all you ever wanted to know about numbers but did not know whom to ask ;) )
by - tsh73 tsh73 , January 2012
Numbers in LB/JB | Integer numbers | Real numbers | Using numbers as Booleans
LB/JB Liberty BASIC has only two data types: string and numbers. So if you have any numerical result it entitled to be “just a number” "just a number". However, “under "under the hood” hood" numbers in any programming language are a bit more complicated. LB/JB Liberty BASIC is not an exception – and has complications (and unique abilities) of its own. To get most of LB/JB, out of Liberty BASIC, you better must understand what’s going on.

Numbers could be integers (AKA whole numbers) – without decimal point – and real numbers (AKA floating point numbers). Besides, since LB/JB Since Liberty BASIC has no Boolean (logical, yes/no true/false) datatype, numbers are used for this purpose as well.

Integer numbers

Numbers Whole numbers like 0, 1, 123, -7 are integers.

Combining integers with + - * MOD operators keep produces a result that is also an integer. Obviously, result of divide a division could end up as a non-integer (having fractional part). Less It is less obvious that power operator operators on integers could end up non-integer: produce non-integer results: for example, 2^(-1) is actually 0.5.
LB/JB
Liberty BASIC has no “integer division” "integer division" operator, but it could be done as INT(a/b), taking only the integer part of result.

Long integers (aka Arbitrary length integers)

Now, there is a somewhat unique strong point in LB/JB: Liberty BASIC: in most of languages, the size of an integer is determined by the underlying computer architecture.
QBasic
QBasic's integer type is stored in 2 bytes, and hence limited to +/- 2^15, from -32,768 to 32,767.

In contemporary C C, an integer is stored in 4 bytes, so it is limited to +/-2^31, from -2,147,483,648 to 2,147,483,647.

To get all 158 digits of 100! (factorial of 100, defined as 1*2*3*...*99*100) in these languages one has to invent things. But things, but for LB/JB number Liberty BASIC, numbers of such length poses pose no problem:
 f=1 
n=100
for i=1 to n
f=f*i
next
print len(str$(f))
print f

output is (line breaks added to the long number for page formatting):
 158 – number of digits 
and factorial itself, hold your breath:
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 9332621544394415268169923885626670049071596826438162146859296389
5217599993229915608941463976156518286253697920827223758251185210
916864000000000000000000000000

It looks like the length of those integers is limited only by available memory and computer speed (of course working with these beasts will be slower!). So getting 10^10000 might take several seconds. But it works, and with all digits!

Functions VAL( ) and STR$( ) in LB/JB Liberty BASIC support long integers.

(Of course) +, -, *, as well as ^ and MOD supports long integers.

  • If a is a long integer and a/b is supposed to be whole number,
    • then a/b will be a long integer
    • else a/b will be a real number (and it even could overflow).
There are some beneficial quirk quirks about INT(a/b):
  • If a is a long integer, and a/b is supposed to be real number,
    • then result will be a long integer (with all digits preserved). Also we evade real overflow.

It looks like LB/JB Liberty BASIC somehow bypasses the intermediate step of storing a/b as real.
It looks like then when calling standard math functions, the argument is converted to real the "real" data type. This way, you can get overflow. So while 10^200 is within double range, you cannot get it by sqr(10^400) – 10^400 will try to convert to real and overflow.

Real numbers

Storing (small) integers are is rather straightforward. But then straightforward but our data or result turns out to be non-integer, its non-integer. It strongly looks like they end up stored in common in some other languages DOUBLE data type. Now, with this This data type came with common limitations (existing in many programming languages):

Range limitation

Here is data on limitation for double-precision numbers from QBasic Help:
  • Positive 1.79769313486231D+308 4.940656458412465D-324
  • Negative -4.940656458412465D-324 -1.79769313486231D+308
If your result exceeds these limits, you’ll get overflow error. In LB/JB, Liberty BASIC, it is easy to get then when working with long integers.

Precision limitation

Double real value stores only about 16 “real” "real" digits. You can check it with USING function:
 mask$="#." 
for i=1 to 20: mask$=mask$+"#": next '20 digits after "."
print using(mask$, 1/3)
print "*.12345678901234567890"
prints
 0.33333333333333331968 
*.12345678901234567890
, that is, we get “garbage” after 16-th digit; or just digit.

Just checking how small number d should be so 1+d will register as d:
 d=1 
for i=1 to 20
d=d/10
a=1+d
print i,1-a
next

Precision limitation consequences

This gives some really bad things you should be aware of (and it is not a bug, it is a way real numbers work in pretty much any language):
  • You should not count on real numbers being exact.
  • You should never test real numbers for being exactly equal.
 a = 2.1 - 2 
b = 0.1
print a, b
if a=b then print "Equal" else print "Not equal"
print "Difference "; a-b
Output:
 0.1 0.1 
Not equal
Difference 0.83266727e-16
Instead, you should test for equality with given precision:
 precision = 1e-10 
if abs(a - b) < precision then print "Equal with precision" else print "Not equal"
  • You should not make FOR loops with real step, if the number of steps is important (like in numerical integration).
 for i = 1 to 2 step 0.1 
print i
next
This example misses the last step (end up on 1.9 instead of 2.0).

Instead, you should use loop by integer index.

Saving/loading real number

There is no way in LB/JB Liberty BASIC to save/load real number in binary representation, making sure we read back exactly what we saved. The only provided way is in text form.

The problem is that PRINT outputs only 7 digits of 16 we could use. If we try VAL, we’ll see that it got has same limitation.
There is
The USING function which could provide the necessary precision, but how to do we get right mask? There no support for scientific notation.
User-defined function Scientific USING function provides such a way.

Using numbers as Booleans

As was stated, LB/JB Liberty BASIC lacks a Boolean type. But type, but it does have conditional statements (of course) which normally has condition. And statements. A condition normally evaluate evaluates to false or true.

If we print result of any relational operator (> < = >= <= <>), we'll see that true prints as 1 and false prints as 0.

This explains construction like eternal loop:
 While 1 
...
Wend
Also it opens possibilities for using a condition result as a number, number in some shortcuts like this one:
 y=x*(x>0) 
(returns x if it is >0, otherwise 0)

The only official rule is " 0 represents false, everything else means true ".

That allows for things like
 If instr(txt$,searchFor$) then 
Meaning the same as
 If instr(txt$,searchFor$)<>0 then 
That is, "if searchFor$ is found in txt$".

However then when working with Boolean expressions involving numbers other then 0 and 1, one should take in into account that logical operators AND OR XOR are bitwise:
 5 or 3 -> 7 (101 or 011 -> 111) 
5 and 3 -> 1 (101 and 011 -> 001)
5 xor 3 -> 6 (101 xor 011 -> 110)
You can examine bits in a number with finction BitPattern$:
 function BitPattern$(num) 
for i = 0 to 31
if i mod 8 = 0 then BitPattern$ = " " + BitPattern$
BitPattern$ = str$((num and 2 ^ i) > 0) + BitPattern$
next i
end function
So it is possible to get
 a=1:b=2 
if a then print "a is true" else print "a is false"
if b then print "b is true" else print "b is false"
if a and b then print "a and b is true" else print "a and b is false"
resulting at
 a is true 
b is true
a and b is false

Also note that some functions (EOF(#handle)) returns functionsreturns -1 for true. One such function is (EOF(#handle))

Function NOT(x), contrary to logical operators, is not bitwise. It returns -1 for true, too. You can get bitwise NOT(x) as (x XOR -1) (it looks like -1 in binary representation has 32 bits set).

There are two exceptions to the rule "0 represents false, everything else means true":
1) Syntax of SELECT CASE allows conditions in CASE, but here only the value 1 is accepted as true, true; others (including -1) are considered false.
 x = 1 
select case
case x: print "true"
case else: print "false"
end select
'prints true

x = -1
select case
case x: print "true"
case else: print "false"
end select
'prints false
2) while real number like 0.1 is surely not 0, you cannot use reals instead of conditions. BASIC just dies at runtime with "isEmpty not understood" error.
 x = .1 
if x then print "true" else print "false" ' dies at runtime with "isEmpty not understood" error.