square root subroutine

toxicmouse

Senior Member
this is a useful little subroutine for those of you not using the x1 parts. it accepts integers fromm 0 to 255.

<code><pre><font size=2 face='Courier'>
'square root subroutine
'1 June 2007, toxicmouse, using picaxe 18x
'60 bytes, excluding main
'WORKS WELL for inputs from 0 to 255
'use terminal to enter an integer from 000 to 255. note, 3 digits are read by the picaxe before calculation, so input 009, not 9.


symbol serial_input = 0

main:
b13 = 0
b12 = 0
serin serial_input, N4800, b13, b12, b11
b13 = b13 - 48 'this converts 3 digits into a 3 digit integer
b12 = b12 - 48
b11 = b11 - 48
b0 = 10 * b12 + b11
b1 = 100 * b13 + b0
gosub square_root
sertxd (#b2,cr,lf)
goto main

'=========================== SUBROUTINE =====================================
square_root: 'the input is b1, output is b2 = sqrt(b1). if no answer, b2 = 0
if b1 = 0 then answer_is_zero 'don't bother running the loop

'this is the first loop simplified
b2 = b1 / 4 + 1

'this is the remaining loops
for b0 = 1 to 6
b4 = b1/b2
b3 = b2 + b4 /2
if b2 = b3 then sqrt_found
b4 = b1 / b3
b2 = b3 + b4 /2
if b2 = b3 then sqrt_found
next b0

b1 = b1 +1 'there is no result found, add 1 to the input and run subroutine again this means that sqrt 8 = 3 and sqrt 15 = 4, etc.
goto square_root

sqrt_found:
return
answer_is_zero:
b2 = 0
return
</font></pre></code>
 

Jeremy Leach

Senior Member
Like it. Here's my flavour where in theory you can insert x^3 etc as the function. The simulator doesn't like the if for some reason though (on second iteration).

<code><pre><font size=2 face='Courier'>
#picaxe 08m

Symbol x = b0
Symbol StepValue = b1
Symbol Fx = w1
Symbol TargetFx = w2
Symbol Iteration = b6


Main:
TargetFx = 45678
Gosub Solve
End

Solve: 'Finds value of x, so that F(x) = TargetFx
x = 128
StepValue = 64

For Iteration = 1 To 8
Fx = x * x '&lt;&lt;&lt; Can use other functions here, e.g x^3

If Fx &gt;= TargetFx Then
x = x - StepValue
Else
x = x + StepValue
EndIf

StepValue = StepValue / 2
Next
Return
</font></pre></code>
 

toxicmouse

Senior Member
well, yours is definately simpler and shorter. nice to have a choice.

i don't suppose you've got an arctan subroutine?

Edited by - toxicmouse on 02/06/2007 13:38:45
 

Jeremy Leach

Senior Member
I haven't, although here's a generic 'graph lookup' routine I've developed. Too big for 08M. I've put in the data for ArcTan (limiting it to a range of 0 to 82.14 degrees). It seems to work pretty well on the simulator. There's a spreadsheet to work out the data values and it's all pretty complex. Could extend it to 0 to 90 degrees if really required by having a second graph (which it supports).

<code><pre><font size=2 face='Courier'>
#picaxe 18x
'############################################################################
'# EEPROM #
'############################################################################

'GRAPH DEFINITION TABLE
'Record0 (ArcTan)
EEPROM 0, (10) 'DataStartAddress (Byte value)
EEPROM 1, (0) 'StartX_ID (Byte value)
EEPROM 2, (30) 'NumYValues (Byte value)
EEPROM 3, (6,1 ) 'X_Interval (Word value)

'ArcTan graph data
EEPROM 10 ,(0,0)
EEPROM 12 ,(245,5)
EEPROM 14 ,(226,10)
EEPROM 16 ,(213,14)
EEPROM 18 ,(251,17)
EEPROM 20 ,(122,20)
EEPROM 22 ,(115,22)
EEPROM 24 ,(2,24)
EEPROM 26 ,(63,25)
EEPROM 28 ,(61,26)
EEPROM 30 ,(10,27)
EEPROM 32 ,(179,27)
EEPROM 34 ,(66,28)
EEPROM 36 ,(189,28)
EEPROM 38 ,(41,29)
EEPROM 40 ,(138,29)
EEPROM 42 ,(225,29)
EEPROM 44 ,(49,30)
EEPROM 46 ,(122,30)
EEPROM 48 ,(187,30)
EEPROM 50 ,(245,30)
EEPROM 52 ,(40,31)
EEPROM 54 ,(85,31)
EEPROM 56 ,(123,31)
EEPROM 58 ,(157,31)
EEPROM 60 ,(189,31)
EEPROM 62 ,(220,31)
EEPROM 64 ,(0,32)
EEPROM 66 ,(45,32)
EEPROM 68 ,(104,32)


'############################################################################
'# DECLARATIONS #
'############################################################################

Symbol PARAM_W0 = 110
Symbol PARAM_0 = 110
Symbol PARAM_1 = 111
Symbol PARAM_W1 = 112
Symbol PARAM_2 = 112
Symbol PARAM_3 = 113

'Graph IDs
Symbol Graph_ArcTan = 0


'############################################################################
'# DEMO #
'############################################################################

Demo:
'Demo's use of Graph lookup for ArcTan.
'The value poked is 1000*the Tan value. MUST be in range 0 to 7250.
'The value returned is 100 * the degree value.
'NOTE: Current graph settings is for angle in range 0 to 82.140 degrees.

Poke PARAM_0,Graph_ArcTan
w0 = 456
Poke PARAM_W1,Word w0
Gosub GraphLookup
Peek PARAM_W0,Word w0
SerTxd (&quot;ArcTan 0.456 = &quot;,#w0)
End


#Rem
'############################################################################
'# GRAPH LOOKUP MODULE. J.Leach March 2007 #
'############################################################################

INTRODUCTION:
This module allows lookup of Y-(Word)values from EEPROM, given a known X-(Word)value.
EEPROM holds Y-values for a set of sequential, evenly spaced X-values, spaced at
X-Interval. The result is an interpolated value of Y between the known points
on the graph:


___-
o -'''
/.
/&#180; .
---------------- o .
Y-Value /&#180;| .
o- | .
/&#180;. | .
/ . | .
/&#180; . | .
/ . | .
/&#180; . | .
. | .
Lower X_ID | Upper X_ID
| | |
| | |
v | v
+-----------+---------+---------+---------+
X_ID: Xn Xn+1 Xn+2 Xn+3 Xn+4 ....

&lt;---------&gt;
X-Interval
^
|
Given X-Value


GRAPH DEFINITION TABLE:
This is an indexed table in EEPROM that holds the following data for each Graph:
- DataStartAddress (Byte Value)
- StartX_ID (Byte Value)
- NumYValues (Byte value)
- X_Interval (Word value)

LIMITATIONS:
- Maximum value X_Interval can take is 655, to avoid overflow in calcs.
- Maximum difference between successive Y_Values is 6553 to avoid overflow in calcs.

NOTES:
1.If the given X_Value is greater than the maximum X_ID defined by the Graph data, then the
Y_Result value is set to be that of the end point on the graph.

2.If the given X_Value is less than the StartX_ID defined by the Graph data, then the Y_Result
value is set to be that of the start point of the graph.

#endrem

Symbol EEPROM_GraphDefinitionTable = 0 'Set it to whatever is conveinient.
Symbol DefinitionBytes = 5 'The number of bytes used by each 'record' of definition
'in the Graph Definition Table.

Symbol LowerIndex = b0 'The index to the lower Y-Value in the EEPROM Graph data.
Symbol EEPROM_Address = b1 'General purpose address variable.
Symbol Graph_ID = b1 'The ID of the graph.
Symbol DataStartAddress = b2 'The start address of the graph data.
Symbol NumYValues = b3 'The number of Y Values stored in EEPROM.
Symbol MaxLowerIndex = b3 'The maximum permissible LowerIndex
Symbol StartX_ID = b4 'The X_ID of the first data point stored in EEPROM.
Symbol LowerX_ID = b5 'The lower X_ID corresponding to the X_Value.
Symbol PercOfInterval = b5 'The percentage that the X-Value is between intervals.
Symbol X_Interval = w3 'The difference between successive X_IDs on the X axis.
Symbol Y_Lower = w4 'The Y value corresponding to the Lower X_ID.
Symbol Y_Upper = w5 'The Y value corresponding to the Upper X_ID.
Symbol Y_Difference = w6 'The absolute difference between Y_Lower and Y_Upper.
Symbol Y_Result = w6 'The result value.
Symbol X_Value = w6 'The given X.

GraphLookup:
'ON ENTRY: PARAM_0 (Graph_ID) holds the index to the Graph to use

' PARAM_W1 (X_Value) is the known X-value (Word).

'ON EXIT: PARAM_W0 (Y_Result) holds the result.


'Load the variables
Peek PARAM_0,Graph_ID
Peek PARAM_W1,Word X_Value

'Get the graph definition values.
EEPROM_Address = DefinitionBytes * Graph_ID + EEPROM_GraphDefinitionTable
Read EEPROM_Address,DataStartAddress,StartX_ID,NumYValues,Word X_Interval

'Determine if Lower X_ID is less than the StartX_ID.
LowerX_ID = X_Value / X_Interval
If LowerX_ID &gt;= StartX_ID Then X_ID_OK

'X_Value is outside the graph data, so set the Y_Result to be that of the
'the start point of the graph.
Read DataStartAddress,Word Y_Result
Return

X_ID_OK:
'Determine the maximum permissible LowerIndex value.
MaxLowerIndex = NumYValues - 2

'Determine the LowerIndex value.
LowerIndex = LowerX_ID - StartX_ID

If LowerIndex &gt; MaxLowerIndex Then
'The X_Value is off the end of the Graph data, so set the Y_Result to be
'that of the end point on the graph.
EEPROM_Address = MaxLowerIndex + 1 * 2 + DataStartAddress
Read EEPROM_Address,Word Y_Result
Else
PercOfInterval = X_Value // X_Interval * 100 / X_Interval

'Get the Lower and Upper Y-values.
EEPROM_Address = LowerIndex * 2 + DataStartAddress
Read EEPROM_Address, Word Y_Lower,Word Y_Upper

'Calculate the result
If Y_Lower &lt;= Y_Upper Then
Y_Difference = Y_Upper - Y_Lower
Y_Result = Y_Difference / 10 * PercOfInterval / 10 + Y_Lower
Else
Y_Difference = Y_Lower - Y_Upper
Y_Result = Y_Difference / 10 * PercOfInterval / 10
Y_Result = Y_Lower - Y_Result
EndIf
Endif

'Save the result
Poke PARAM_W0,Word Y_Result
Return
</font></pre></code>



Edited by - jeremy leach on 02/06/2007 17:56:13
 

toxicmouse

Senior Member
nice, i needed the angle, this works nicely for even large numbers circa 20000. i am not too concerned about the accuracy of the angle, but it comes out to be accurate to about 2degrees, more than enough for me.

<code><pre><font size=2 face='Courier'>
'arctan subroutine
'1 June 2007, WORKS, COMPREHENSIVE TESTING NOT DONE.
'191 bytes, excluding main.
'this uses a lookup table to get an angle.
'the output is in bygrees (byte degrees), 1,5 degrees = 1 bygree. 1 revolution = 240 bygrees


symbol serial_input = 0

'input x and y: w1 and w2. output = angle b1
'note that in arctan_lookup, the start of the EEPROM lookup must be inserted in b1
'uses W0,W1,W2

main:
'for testing use terminal on laptop
' serin serial_input, N4800, b13,b12,b11,b10
' b13 = b13 - 48
' b12 = b12 - 48 'this converts 4 digits into a 4 digit integer
' b11 = b11 - 48
' b10 = b10 - 48
' w1 = 10*b11 + b10
' w3 = 100*b12 + w1
' w1 = 1000*b13 + w3
'
' serin serial_input, N4800, b13,b12,b11,b10
' b13 = b13 - 48
' b12 = b12 - 48 'this converts 4 digits into a 4 digit integer
' b11 = b11 - 48
' b10 = b10 - 48
' w2 = 10*b11 + b10
' w3 = 100*b12 + w2
' w2 = 1000*b13 + w3
'
' gosub arctan
' sertxd (#b1,cr,lf)
' goto main

'======================================= ARCTAN SUBROUTINE ==================================
'possible problems:
'1:
'in arctan_1 to arctan_2_3 if the denominator is too large the the results may be inaccurate or incorrect. this would mainly be for arctan 1_3 and 2_3 where the numbers are going to be large anyway. one solution might be to divide the denominator and then divide the numerator after multiplying the scaling factor in part, so say denominator is 30000 and the numerator is 10000, use arctan_1_2:
'4*10000/30000= 1.33
'picaxe error will be 0.33, then multiply that error by 64 = error 21 on the lookup table, which is approx 4.5 degrees.
'2:
'between arctan_1 and arctan_1_1 there could be
'w0 = 128* w2/w1 * 2, and
'w0 = 64* w2/w1 * 4 , and
'w0 = 32* w2/w1 * 8
'all these would reduce the error for values of w2 of 256 to 2048, the most common realistic values of w2.

EEPROM 1, (6,13,20,27,33,40,47,54,61,68,75,82,90,98,103,113,122,130,138,147,156,166,175,185,196,206,218,230,242,255)


arctan:
if w2 &gt; w1 then arctan_2
if w1 &gt; w2 then arctan_1
'if w1 = w2
b1 = 30 '45 degrees
return
'------------------------
arctan_1:
if w2 &gt; 255 then arctan_1_1 'there will be overspill if multiplied by 255, so multiply by 16
w0 = 255 * w2 / w1 'w2/w1 is the ratio in the arctan, 255 is a scaling factor in the lookup table to reduce decimals.
goto arctan1_lookup
arctan_1_1:
if w2 &gt; 4096 then arctan_1_2 'there will be overspill if multiplied by 16, so multi by 4
w0 = 16* w2/w1 *16 'this should limit the overspill. 16*16 = 256, the scaling factor
goto arctan1_lookup
arctan_1_2:
if w2 &gt; 16383 then arctan_1_3 'there will be overspill if multiplied by 4, so multi by 2
w0 = 4* w2/w1 *64 'this should limit the overspill. 4*64 = 256, the scaling factor
goto arctan1_lookup
arctan_1_3:
if w2 &gt; 32768 then arctan_error 'there will be overspill if multiplied by 2
w0 = 2* w2/w1 *128 'this should limit the overspill. 2*128 = 256, the scaling factor
arctan1_lookup:
gosub arctan_lookup
return 'b1 is the angle in bygrees, 1 &lt; b1 &lt;= 30
'-------------------------
arctan_2:
if w1 &gt; 255 then arctan_2_1 'there will be overspill if multiplied by 255, so multiply by 16
w0 = 255 * w1 / w2 'w1/w2 is the ratio in the arctan, 255 is a scaling factor in the lookup table to reduce decimals.
goto arctan2_lookup
arctan_2_1:
if w1 &gt; 4096 then arctan_2_2 'there will be overspill if multiplied by 16, so multi by 4
w0 = 16* w1/w2 *16 'this should limit the overspill. 16*16 = 256, the scaling factor
goto arctan2_lookup
arctan_2_2:
if w1 &gt; 16383 then arctan_2_3 'there will be overspill if multiplied by 4, so multi by 2
w0 = 4* w1/w2 *64 'this should limit the overspill. 4*64 = 256, the scaling factor
goto arctan2_lookup
arctan_2_3:
if w1 &gt; 32768 then arctan_error 'there will be overspill if multiplied by 2
w0 = 2* w1/w2 *128 'this should limit the overspill. 2*128 = 256, the scaling factor
arctan2_lookup:
gosub arctan_lookup


if b1 = 255 then arctan_error 'arctan_error does a return, so from a gosub it would end up here.
b1 = 60 - b1

return 'b1 is the angle in bygrees, 30 &lt; b1 &lt; 59. 60 is 90 degrees
'===========================
arctan_lookup:
if w0 &gt; 256 then arctan_error 'this should never be the case, written for testing.

b1 = 1 'start of the EEPROM address
arctan_lookup_1:
read b1, b2
if b0 &lt; b2 then arctan_lookup_found
b1 = b1 + 1
if b1 &gt; 30 then arctan_error 'note, 30+ start location in EEPROM
goto arctan_lookup_1
arctan_lookup_found:
'b1 'angle = lookup address position = angle in bygrees. 1 &lt; b1 &lt;= 30 bygrees
return
'----------------------------

arctan_error: 'one of the numbers is too large to deal with. any integer over 32768 is too large
b1 = 255
return
'======================================= end of arctan subroutine ==================================






</font></pre></code>
 
Top