Older Version
Newer Version
tsh73
Jan 23, 2012
- "initial placement (was composed and checked in Word)"
=Numbers in LB/JB= (all you ever wanted to know about numbers but did not know whom to ask ;) ) //by [[user:tsh73]]// [[toc|flat]] [[toc]] ---- LB/JB has only two data types: string and numbers. So if you have any numerical result it entitled to be “just a number” However, “under the hood” numbers in any programming language are a bit more complicated. LB/JB is not an exception – and has complications (and unique abilities) of its own. To get most of LB/JB, you better 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 has no Boolean (logical, yes/no true/false) datatype, numbers are used for this purpose as well. =Integer numbers= Numbers like 0, 1, 123, -7 are integers. Combining integers with + - * MOD operators keep result integer. Obviously, result of divide could end up non-integer (having fractional part). Less obvious that power operator on integers could end up non-integer: for example, 2^(-1) is actually 0.5. LB/JB has no “integer division” operator, but it could be done as INT(a/b), taking only integer part of result. ==Long integers (aka Arbitrary length integers)== Now, there is a somewhat **unique strong point** in LB/JB: in most of languages, size of integer is determined by underlying computer architecture. QBasic integer stored in 2 bytes, and hence limited to +/- 2^15, from -32,768 to 32,767. In contemporary C integer stored in 4 bytes, so it 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 for LB/JB number of such length poses no problem: [[code format="lb"]] f=1 n=100 for i=1 to n f=f*i next print len(str$(f)) print f [[code]] output is [[code]] 158 – number of digits and factorial itself, hold your breath: 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 [[code]] It looks like the length of those integers 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 support long integers. (Of course) +, -, *, as well as ^ and MOD supports long integers. > If a is long integer and a/b supposed to be whole number, >> then a/b will be long integer >> else a/b will be real number (and it even could overflow). There some beneficial quirk about INT(a/b): > If a is long integer, and a/b supposed to be real number, >> then result will be long integer (with all digits preserved). Also we evade real overflow. It looks like LB/JB somehow bypasses intermediate step of storing a/b as real. It looks like then calling standard math functions, argument is converted to 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 rather straightforward. But then our data or result turns to be non-integer, its strongly looks like they end up stored in common in some other languages DOUBLE data type. Now, with 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, it is easy to get then working with long integers. ==Precision limitation== Double real value stores only about 16 “real” digits. You can check it with USING function: [[code format="lb"]] mask$="#." for i=1 to 20: mask$=mask$+"#": next '20 digits after "." print using(mask$, 1/3) print "*.12345678901234567890" [[code]] prints [[code]] 0.33333333333333331968 *.12345678901234567890 [[code]] , that is, we get “garbage” after 16-th digit; or just checking how small number d should be so 1+d will register as d: [[code format="lb"]] d=1 for i=1 to 20 d=d/10 a=1+d print i,1-a next [[code]] ==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.** [[code format="lb"]] a = 2.1 - 2 b = 0.1 print a, b if a=b then print "Equal" else print "Not equal" print "Difference "; a-b [[code]] Output: [[code]] 0.1 0.1 Not equal Difference 0.83266727e-16 [[code]] Instead, you should test for equality with given precision: [[code format="lb"]] precision = 1e-10 if abs(a - b) < precision then print "Equal with precision" else print "Not equal" [[code]] * **You should not make FOR loops with real step, if number of steps is important** (like in numerical integration). [[code format="lb"]] for i = 1 to 2 step 0.1 print i next [[code]] This example misses 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 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 same limitation. There is USING function which could provide necessary precision, but how to get right mask? There no support for scientific notation. User-defined function [[http://justbasic.wikispaces.com/Scientific+Using|Scientific USING function]] provides such a way. =Using numbers as Booleans= As was stated, LB/JB lacks Boolean type. But it does have conditional statements (of course) which normally has condition. And condition normally evaluate 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: [[code format="lb"]] While 1 ... Wend [[code]] Also it opens possibilities for using condition result as a number, in some shortcuts like this one: [[code format="lb"]] y=x*(x>0) [[code]] (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 [[code format="lb"]] If instr(txt$,searchFor$) then [[code]] Meaning the same as [[code format="lb"]] If instr(txt$,searchFor$)<>0 then [[code]] That is, "if searchFor$ is found in txt$". However then working with Boolean expressions involving numbers other then 0 and 1, one should take in account that logical operators AND OR XOR are bitwise: [[code]] 5 or 3 -> 7 (101 or 011 -> 111) 5 and 3 -> 1 (101 and 011 -> 001) 5 xor 3 -> 6 (101 xor 011 -> 110) [[code]] You can examine bits in a number with finction BitPattern$: [[code format="lb"]] 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 [[code]] So it is possible to get [[code format="lb"]] 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" [[code]] resulting at [[code]] a is true b is true a and b is false [[code]] Also note that some functions (EOF(#handle)) returns -1 for true. 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 1 accepted as true, others (including -1) considered false. [[code format="lb"]] 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 [[code]] 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. [[code format="lb"]] x = .1 if x then print "true" else print "false" ' dies at runtime with "isEmpty not understood" error. [[code]] ---- [[toc|flat]]