Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Annoying Variable 1

Status
Not open for further replies.

rogr

Programmer
Jun 18, 2002
3
GB
I've written a program in qbasic and it all works apart from one part...I have given a variable the value -100, and at the end of the loop that follows I am adding .01 to the variable, I want the loop to stop when the variable's value reaches 100.01, simple enough but as the program runs the variable value is in the format (x)xx.xxxxx, instead of the (x)xx.xx that I would have hoped for so the loop fails to stop!
Why won't it simlpy add the .01 and keep to 2dp??

The code:
cubicx = -100
DO UNTIL cubicx = 100.01 OR cubicroot3 <> 0 OR INKEY$ = CHR$(27)
IF (cubica * cubicx ^ 3) + (cubicb * cubicx ^ 2) + (cubicc * cubicx) + cubicd = 0 THEN
IF cubicroot1 = 0 THEN
cubicroot1 = cubicx
ELSE
IF cubicroot2 = 0 THEN
cubicroot2 = cubicx
ELSE
cubicroot3 = cubicx
END IF
END IF
END IF
cubicx = cubicx + .01
COLOR 1
LOCATE 16, 3: PRINT &quot;Testing: &quot;; cubicx; &quot; &quot;
COLOR 9
LOOP

What I think it may be doing is adding 0.09999, instead of 0.1, can I make this 0.1 absolute and totally accurate?
 
You do not indicate if you had DIM'd cubicx as a single or double. I'd force it double with a:

Dim Cubicx as Double

Technical rationale:

1. You are close to the limit for &quot;precision&quot; for singles.

2. Single precision aritmetic is &quot;seriously&quot; affected by round-off errors. Therefore, you might want to range check the terminal test. If xxx > ??? and xxx < ??? then

bsh3
 
Or you could test if cubicx >= 100.01, it will stop when it's 100.01001 or something

Tominator
 
yeah, i cud use the >= to stop it, but i want to test the values at step 0.01, so even tho it wud actually stop, the program wud test the wrong values!
cheers anyway.....i'll try the Double thing!
 
I tried the double and it just doubled the problem..with more decimal places to screw up.

I have now updated my loop to:


cubicx = -9.999999
DO
IF (cubica * cubicx ^ 3) + (cubicb * cubicx ^ 2) + (cubicc * cubicx) + cubicd = 0 THEN
IF cubicroot1 = 0 THEN
cubicroot1 = cubicx
ELSE
IF cubicroot2 = 0 THEN
cubicroot2 = cubicx
ELSE
cubicroot3 = cubicx
END IF
END IF
END IF
cubicx = cubicx + .000001
COLOR 1
LOCATE 17, 3: PRINT &quot;Testing: &quot;; cubicx; &quot; &quot;
COLOR 9
LOOP UNTIL cubicx = 9.999999 OR cubicroot3 <> 0 OR INKEY$ = CHR$(27)


so it now works and will, in theory, stop. but to run from start to conclusion would take about 5.5 days (i think, approx.) so i take it that single length variables are to 8 characters including dp or sumthin like that?! which wud make, on the whole, the numbers i am using to 7sf, and/or 6dp!
well is there any number type, apart from integer (i require decimals) that is less than this? or any way to assign a variable a length? or a special character that i can put at the end of the number i assign to this variable to stop it overflowing this length? or am i stuck!?
 
It surely be a way to get in on. Integers are 2 bytes long; that mean &HFFFF(65535 including the 0).
When the integer is signed, the upper bite is used as the sign so instead of 65535, you'll drop to 32766-­­­>

UNSIGNED INTEGER
2 BYTES-> FFFFh = 65535
16 BITES-> 1111 1111 1111 1111b
SIGNED INTEGER
2 BYTES-> 7FFFh = 32767
15 BITES-> [1]111 1111 1111 1111b
The upper bite serve as a signe flag(0=+, 1=- I think)

But anyways, Integer speak by itself; it should be use as integer.

I can't say nothing much about that. Maybe logiclrd can resolve the problem? ;-)



 
Using your first example, have you considered using something like:

DO UNTIL (CINT(cubicx * 100)) = 10001 OR cubicroot3 <> 0 OR INKEY$ = CHR$(27)
 
rogr: Your problem is that [tt]SINGLE[/tt] and [tt]DOUBLE[/tt] do not use base 10 to store the number, but rather base 2. There are some decimal values -- fairly simple ones, even -- that cannot be stored in base 2. An example of how this could be impossible:

The number 1/3 could be stored as 0.1 is base 3 (2/3 would be 0.2 and 3/3 would be 1.0). However, in base 10, you would need an infinite number of digits to store the 1/3 (0.33333333....).

An example of a decimal number that can't be stored by a [tt]SINGLE[/tt] or even a [tt]DOUBLE[/tt] is 0.99. It seems like a fairly simple number, but the closest a [tt]SINGLE[/tt] can get is roughly 0.9899999.

As for your code, your method for finding cubic roots most likely will not work, because if the roots are not exact multiples of 1/100, the expression will never be exactly 0. What you instead want to check for is the expression crossing the boundary. You should also check its derivative, especially if it has a zero that immediately bounces back (the way that y=x^2 has a zero at x=0 but doesn't cross the X axis). Basically, if the derivative changes signs, then you should do a binary search within the range to find the local minimum or maximum. Then, since even doing a search this tightly will likely not yield exactly 0, you need to check the result against an epsilon -- an error threshold -- and report it as a probable zero if it is less than that threshold.

The following code implements this:
[tt]
'f(x) = (x - a)(x - b)(x - c)
'f(x) = (x + 5.7293)(x - 3)(x - 3)

a# = -5.7293 'these are the roots
b# = 3
c# = 3

'f(x) = x^3 - (a + b + c)x^2 + (ab + ac + bc)x - abc
'f(x) = Ax^3 + Bx^2 + Cx + D

cubicA# = 1#
cubicB# = -(a# + b# + c#)
cubicC# = a# * b# + a# * c# + b# * c#
cubicD# = -a# * b# * c#

DIM root#(3), rootDouble%(3)
numRoots% = 0

'Derivative f'(x) = 3Ax^2 + 2Bx + C
quadraticA# = 3# * cubicA#
quadraticB# = 2# * cubicB#
quadraticC# = cubicC#

epsilon# = .00000001# 'error threshold

x# = -100#
thisCubic# = cubicA# * x# ^ 3# + cubicB# * x# ^ 2# + cubicC# * x# + cubicD#
thisQuadratic# = quadraticA# * x# ^ 2# + quadraticB# * x# + quadraticC#

FOR i% = -9999 TO 10000
lastX# = x#
x# = i% / 100# 'this way, we preserve accuracy

lastCubic# = thisCubic#
lastQuadratic# = thisQuadratic#

thisCubic# = cubicA# * x# ^ 3# + cubicB# * x# ^ 2# + cubicC# * x# + cubicD#
thisQuadratic# = quadraticA# * x# ^ 2# + quadraticB# * x# + quadraticC#

IF thisCubic# = 0# THEN 'very unlikely unless the roots are multiples
numRoots% = numRoots% + 1 'of 1/2 or 1/5 , but could happen
root#(numRoots%) = x#
IF numRoots% = 3 THEN EXIT FOR
ELSEIF SGN
(thisCubic#) * SGN(lastCubic#) < 0 THEN
lowBoundary# = lastX#
highBoundary# = x#
FOR j% = 1 TO 25 'higher is more precise, but unnecessary
testX# = lowBoundary# / 2# + highBoundary# / 2# '<-- slower, more accurate
testCubic# = cubicA# * testX# ^ 3# + cubicB# * testX# ^ 2# + cubicC# * testX# + cubicD#
IF SGN(testCubic#) = SGN(lastCubic#) THEN
lowBoundary# = testX#
ELSE
highBoundary# = testX#
END IF
NEXT
j%
numRoots% = numRoots% + 1
root#(numRoots%) = lowBoundary# / 2# + highBoundary# / 2#
IF numRoots% = 3 THEN EXIT FOR
ELSEIF SGN
(thisQuadratic#) * SGN(lastQuadratic#) < 0 THEN
IF
lastCubic# = 0 THEN
rootDouble%(numRoots%) = -1
ELSE
testX# = lastX# / 2# + x# / 2
testCubic# = cubicA# * testX# ^ 3# + cubicB# * testX# ^ 2# + cubicC# * testX# + cubicD#
IF ABS(testCubic#) < ABS(thisCubic#) THEN 'it is closer to zero in the middle
lowBoundary# = lastX#
highBoundary# = x#
FOR j% = 1 TO 25 'higher is more precise, but unnecessary
testX# = lowBoundary# / 2# + highBoundary# / 2# '<-- slower, more accurate
testQuadratic# = quadraticA# * testX# ^ 2# + quadraticB# * testX# + quadraticC#
IF SGN(testQuadratic#) = SGN(lastQuadratic#) THEN
lowBoundary# = testX#
ELSE
highBoundary# = testX#
END IF
NEXT
j%
testX# = lowBoundary# / 2# + highBoundary# / 2#
testCubic# = cubicA# * testX# ^ 3# + cubicB# * testX# ^ 2# + cubicC# * testX# + cubicD#
IF ABS(testCubic#) < epsilon# THEN
numRoots% = numRoots% + 1
root#(numRoots%) = testX#
rootDouble%(numRoots%) = -1
IF numRoots% = 3 THEN EXIT FOR
END IF
END IF
END IF
END IF
NEXT
i%

PRINT &quot;Roots found:&quot;; numRoots%
FOR i% = 1 TO numRoots%
IF rootDouble%(i%) THEN
PRINT
&quot;Double root at: &quot;;
ELSE
PRINT
&quot;Root at: &quot;;
END IF
PRINT
root#(i%)
NEXT i%
[/tt]
 
&quot;You can have perfect precision&quot;

Here is something that might inspire you.

DIM Converter(7) AS STRING

CLS

Number$ = &quot;100.00&quot;
PRINT Number$: PRINT


Repeat:
FOR Looper = 1 TO 6
Converter(Looper) = MID$(Number$, Looper, 1)
NewNumber = ASC(Converter(Looper))
IF Looper = 6 THEN NewNumber = NewNumber + 1
FinalNumber$ = FinalNumber$ + CHR$(NewNumber)
NEXT Looper

Number$ = FinalNumber$
FinalNumber$ = &quot;&quot;
PRINT Number$

IF MID$(Number$, 6, 1) <= &quot;8&quot; THEN GOSUB Repeat

 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top