COS

From BeebWiki
Jump to: navigation, search

COS is a BASIC function which returns the Cosine of the angle (passed as a parameter).

COS
Availability Present in all original versions of BBC BASIC.
Syntax BASIC I-V <num-var> = COS(<numeric>)
Token (hex) BASIC I-V 9B (function)
Description BASIC I-V Returns the Cosine of the angle (passed as a parameter)
Associated keywords SIN, TAN, ATN, ASN, ACS

Contents

Description

COS accepts a single parameter (the angle), and returns the Cosine of that angle.


Internal Operation

Overview

COS(x)
If the absolute value of the parameter (x) is more than 8388607 (i.e. the exponent of the Floating-Point value is larger than 151 (#&97)), Error 'Accuracy Lost' will be generated.

Test that the parameter (x) is within the required range: -0.785398 to 0.785398 [i.e. -(PI/4) to (PI/4)]

The test is done by adding PI/4 (0.785398) to the parameter and multiplying the result by 2/PI (0.636619772) - let's call this value y. This result is then converted to an Integer value - let's call this value z. If the result is less than 1 (i.e. 0), then the value is in the required range, otherwise, z indicates how far the value lies from the range.

If the value is not within the required range, the value must be reduced so that it falls within the range. This is done by multiplying -1.57080078 by the value z, adding the result of this calculation to the original parameter x value (modifying the value of x); then multiplying 0.00000445445511 by the value z and adding the result of this calculation to the parameter x value (modifying the value of x). The value x will now be within the required range. Note: subtracting 0.00000445445511 from 1.57080078 results in 1.57079632554489 (which is PI/2).


Next, square the value x - Let's call this value w.

Now, we need to calculate the Cosine Fraction (the value we need to modify the value x by to obtain the Cosine value). Let's call this Cosine Fraction s.

If w is less than 2E-20 (or thereabouts - i.e. the Floating Point exponent value is less than #&40), then use 1.0 as the Cosine Fraction (s) value, as the parameter x value is too small to evaluate, so the result that is returned in this case will be x.

Calculate the reciple of the value w - let's call this value u [u = 1/w].
v = -0.0119090311 + u

Cycle 1:
v = 0.000107499459 / v
v = v + -0.0171640246
v = v + u (reciple value of w)

Cycle 2:
v = 0.0013095369 / v
v = v + 0.0499999922
v = v + u (reciple value of w)

Last Cycle:
v = -0.166666666 / v
v = v + 1.0

The Cosine Fraction (s) is the result of the above cycle calculations - i.e. v; therefore --> s = v.

Multiply s by the parameter value x [s = s * x]

Next, increment the value z by 1. If z is an even number then adjust the calculated s value as follows: Square the value s, subtract this result from 1.0, and set the s value to the squareroot (SQR) of the result [i.e.: s = SQR(1.0 - (s * s))].

Note: Sine checks for z being an odd value, and cosine checks for an even value, before carrying out the adjustment described above.

Lastly, if the value z is divisible by 2 (but not by a power of 2 - i.e. 4, 8, 16, 32, 64; or a combination of these powers - i.e. 12 (4 + 8), etc...), the Cosine result is the complement of s [COS(x) = -s]; otherwise, the Cosine result is s [COS(x) = s].

Note: Incrementing z by 1 (with the above calculations) helps to alter the s value so that when the value is adjusted, the cosine result will be generated, rather than the Sine result.


Detailed

BASIC 4 implements COS as follows:

On entry to the COS routine, the carry flag will be set - this indicates that the Cosine result is required (a clear carry flag indicates that the Sine result is required!).

Routine &A93A does the following:

  • Call routine &96DA to obtain the value at the Text pointer B location (convert it to Float if it is Integer or issue 'Type mismatch' error if it is a String value).
  • If the FWA exponent (location &30) is more than or equal to #&98 then issue 'Accuracy lost' error as the FWA value is too large to use to calculate the SIN/COS value.
  • Store the FWA value to temporary Floating point value location &046C-&0470.
  • Call &A4E0 to unpack the Floating-Point constant at &BF2E (1.57079633, which is PI/2) to the FWB
  • Store the FWA sign (location &2E) in the FWB sign byte (location &3B) so that the PI/2 value has the same sign as the argument (the FWA value).
  • Decrement the FWB exponent (location &3C) to divide the FWB value by 2 - so the FWB now contains the value 0.785398165.
  • Call routine &A692 to add the FWB value (PI/4) to the FWA value.
  • Set A to #&33.
  • Call routine &A9D4 to set the argument pointer (&4A-&4B) to &BF00 + A = &BF33 (which points to the Float value 0.636619772 (which is 2/PI) in the Floating-Point constant table) and then multiply the FWA by the Float value pointed to by the argument pointer (&4A-&4B).
  • Call routine &96C3 to convert the Float value (FWA) to an Integer and place the result in the IWA.
  • Store A (the LSB of the IWA value - from location &2A) in location &49.
  • If the Integer value (&2A-&2C) is zero then jump to &A98D (as the FWA value does not need to be reduced to a value within the required range -(PI/4) to (PI/4) [i.e. -0.785398 to 0.785398]), so [&A98D] load the FWA with the original Sine/Cosine argument (from location &046C-&0470).
  • Otherwise, the FWA value is out of the Sine/Cosine range, and needs to be reduced, so:
    • Call &8189 to convert the Integer value (IWA) to a Float and store the result in the temporary Floating-point variable located at &0471-&0475.
    • Then set the argument pointer (&4A-&4B) to pointer to &BF24 (which is the Floating-point constant value -1.57080078) and multiply the FWA by this constant.
    • Set the argp pointer (&4A-&4B) to point to the temporary Floating-point variable location &046C (which is the original FWA Sine/Cosine argument value) and add the Floating-Point value at &046C-&0470 to the FWA value.
    • Store the result in temporary Floating-Point value location &046C-&0470.
    • Set the FWA to the Floating-Point value stored at location &0471-&0475 (the IWA value).
    • Set the argument pointer (&4A-&4B) to point to &BF29 (the address of the value 0.00000445445511 in the Floating-Point constant table).
    • Multiply the FWA by the Floating-Point constant 0.00000445445511 (store the result in the FWA).
    • Set the argument pointer (&4A-&4B) to &046C and add the Floating-Point value at &046C-&0470 to the FWA.
    • Jump to &A990 to perform the Cosine calculation as the value is now within the required range.



  • [A990] Store the FWA value (the actual Cosine argument, which has been reduced where necessary) to location &0476-&047A.
  • Multiply the FWA by the Floating-point value at &0476-&047A (i.e. square the FWA value).
  • Set X to #&74 (i.e. the LSB of the first fraction to apply)
  • Set A to #&92 (i.e. the LSB of the default value (if the FWA value is too small to evaluate)
  • Set Y to #&02 (the number of continued-fraction expansion cycles to evaluate)
  • Call routine &A861 to evaluate the continued-fraction expansions, as follows:
    • If the FWA exponent is less than #&40 then the FWA value is too small to evaluate, so exit with the FWA set to the Floating-Point constant at &BF92 (i.e. 1.0).
    • Calculate the reciple of the FWA value (FWA = 1/FWA) and store the result in the FWA and location &046C-&0470
    • Add the Floating-Point constant at &BF74 (-0.0119090311) to the FWA
    • Cycle 1:
    • Divide the Floating-Point constant at &BF79 (0.000107499459) by the FWA, storing the result in the FWA.
    • Add the Floating-Point constant at &BF7E (-0.0171640246) to the FWA.
    • Add the Reciple value (from location &046C-&0470) to the FWA.
    • Cycle 2:
    • Divide the Floating-Point constant at &BF83 (0.0013095369) by the FWA, storing the result in the FWA.
    • Add the Floating-Point constant at &BF88 (0.0499999922) to the FWA.
    • Add the Reciple value (from location &046C-&0470) to the FWA.
    • Last Cycle calculation:
    • Divide the Floating-Point constant at &BF8D (-0.166666666) by the FWA, storing the result in the FWA.
    • Add the Floating-Point constant at location &BF92 (1.0) to the FWA.
  • Multiply the FWA by the Temporary Floating-Point variable stored at location &0476-&047A (the argument value).
  • Exit with A = #&FF (as the result is a Floating-Point value in the FWA)


Now, the FWA contains either the Sine result or the Cosine result (which will have to be converted to the value requested by the user).

Retrieve the carry flag value from the 6502 stack. As the carry flag is set (Cosine result required), increment the value stored at location &49 (the integer part of the FWA argument value).


[&A923] If bit 0 of location &49 is clear (i.e. the original argument was an even value (in the case of Sine) - or an odd value (in the case of Cosine)) then Set A to #&FF (to indicate that the result is a Floating-point value) and [&A917] check the value in location &49 - if bit 1 is not set then complement the FWA value (FWA=-FWA) and exit; otherwise, just exit.

Otherwise, bit 0 of location &49 is set (indicating that the original argument was odd (in the case of Sine) or even (in the case of Cosine); so, adjust the calculated result as follows:

Square the FWA value (FWA = FWA * FWA),
Subtract the FWA value from 1 (FWA=1.0-FWA) and
Set the FWA value to the square root of the FWA (FWA=SQR(FWA)).

[&A917] check the value in location &49 - if bit 1 is not set then complement the FWA value (FWA=-FWA) and exit; otherwise, just exit.

Example 1: COS(1.5)

Set &046C = 1.5 (the argument)

Set FWB to 0.785398165 (PI/4)

FWA = FWA + FWB = 2.285398

FWA = FWA * 0.636619772 = 1.4549294

?&49 = 1 (Integer part of FWA value). FWA = IWA = 1

Store the FWA in &0471

FWA = FWA * -1.57080078 = -1.57080078

FWA = FWA + &046C = -0.07080078

Store FWA in &046C

Store &0471 (1) in FWA

FWA = FWA * 0.0000044544511 = 0.0000044544511

FWA = FWA + &046C = -0.0707963255

Store the FWA in &0476

FWA = FWA * FWA = 0.0050121197

[&A861]: FWA = 1 / FWA = 199.51638

Store FWA in &046C

FWA = FWA + -0.0119090311 = 199.504474778

FWA = 0.000107499 / FWA = 0.00000005388

FWA = -0.017164024 + FWA = -0.017163485

FWA = &046C + FWA = 199.499216515

FWA = 0.0013095369 / FWA = 0.000006564

FWA = 0.04999999 + FWA = 0.050006554

FWA = &046C + FWA = 199.566387

FWA = -0.16666666 / FWA = -0.00083514

FWA = 1 + FWA = 0.999164856

FWA = FWA * &0476 = -0.070737200377


Increment &49 --> now &49 = 2

Bit 0 of &49 is clear and Bit 1 of &49 is set, indicating that the FWA value needs to be complemented - so exit with FWA = - FWA = 0.070737200377

Example 2: COS(-0.75)

Set &046C = -0.75 (the argument)

Set FWB to -0.785398165 -(PI/4)

FWA = FWA + FWB = -1.535398165

FWA = FWA * 0.636619772 = -0.9774646

?&49 = 0 (Integer part of FWA value). FWA = IWA = 0

[&A98D] As &49 is 0, store value at &046C in FWA = -0.75

Store the FWA in &0476

FWA = FWA * FWA = 0.5625

[&A861]: FWA = 1 / FWA = 1.777777

Store FWA in &046C

FWA = FWA + -0.0119090311 = 1.7896868

FWA = 0.000107499 / FWA = 0.00006

FWA = -0.017164024 + FWA = -0.0171039

FWA = &046C + FWA = 1.7606737

FWA = 0.0013095369 / FWA = 0.0007437

FWA = 0.04999999 + FWA = 0.0507436

FWA = &046C + FWA = 1.8285213

FWA = -0.16666666 / FWA = -0.0911482

FWA = 1 + FWA = 0.9088517

FWA = FWA * &0476 = -0.6816387


Increment &49 -> now &49 = 1

Bit 0 of &49 is set, so adjust the calculated value as follows:

FWA = FWA * FWA = 0.4646313
FWA = 1 - FWA = 0.5353686
FWA = SQR(FWA) = 0.7316889

Bit 1 of &49 is not set, so exit with FWA unchanged.



Example 3: COS(0.0)

Set &046C = 0.0 (the argument)

Set FWB to 0.785398165 (PI/4)

FWA = FWA + FWB = 0.785398165

FWA = FWA * 0.636619772 = 0.50000000

?&49 = 0 (Integer part of FWA value). FWA = IWA = 0

[&A98D] As &49 is 0, store value at &046C in FWA = 0.0

Store the FWA in &0476

FWA = FWA * FWA = 0.0

[&A861]: FWA = 1.0 (as argument is zero)

FWA = FWA * &0476 = 0.0


Increment &49 -> now &49 = 1

Bit 0 of &49 is set, so adjust the calculated value as follows:

FWA = FWA * FWA = 0.0
FWA = 1 - FWA = 1.0
FWA = SQR(FWA) = 1.0

Bit 1 of &49 is not set, so exit with FWA unchanged.


Example 4: COS(0.25)

Set &046C = 0.25 (the argument)

Set FWB to 0.785398165 (PI/4)

FWA = FWA + FWB = 1.0353982

FWA = FWA * 0.636619772 = 0.6591548

?&49 = 0 (Integer part of FWA value). FWA = IWA = 0

[&A98D] As &49 is 0, store value at &046C in FWA = 0.25

Store the FWA in &0476

FWA = FWA * FWA = 0.0625

[&A861]: FWA = 1 / FWA = 16

Store FWA in &046C

FWA = FWA + -0.0119090311 = 15.988091

FWA = 0.000107499 / FWA = 0.0000067

FWA = -0.0171640246 + FWA = -0.0171572

FWA = &046C + FWA = 15.982843

FWA = 0.0013095369 / FWA = 0.0000819

FWA = 0.04999999 + FWA = 0.0500818

FWA = &046C + FWA = 16.050082

FWA = -0.16666666 / FWA = -0.0103841

FWA = 1 + FWA = 0.9896158

FWA = FWA * &0476 = 0.2474039


Increment &49 -> now &49 = 1

Bit 0 of &49 is set, so adjust the calculated value as follows:

FWA = FWA * FWA = 0.0612086
FWA = 1 - FWA = 0.9387913
FWA = SQR(FWA) = 0.9689124

Bit 1 of &49 is not set, so exit with FWA unchanged.



Example 5: COS(2.41)

Set &046C = 2.41 (the argument)

Set FWB to 0.785398165 (PI/4)

FWA = FWA + FWB = 3.195398

FWA = FWA * 0.636619772 = 2.0342533

?&49 = 2 (Integer part of FWA value). FWA = IWA = 2

Store the FWA in &0471

FWA = FWA * -1.57080078 = -3.1416014

FWA = FWA + &046C = -0.7316014

Store FWA in &046C

Store &0471 (2) in FWA

FWA = FWA * 0.0000044544511 = 0.0000089

FWA = FWA + &046C = -0.7315924

Store the FWA in &0476

FWA = FWA * FWA = 0.5352275

[&A861]: FWA = 1 / FWA = 1.8683641

Store FWA in &046C

FWA = FWA + -0.0119090311 = 1.8564551

FWA = 0.000107499 / FWA = 0.0000057

FWA = -0.017164024 + FWA = -0.0171583

FWA = &046C + FWA = 1.8512058

FWA = 0.0013095369 / FWA = 0.0007073

FWA = 0.04999999 + FWA = 0.0507072

FWA = &046C + FWA = 1.9190714

FWA = -0.16666666 / FWA = -0.0868475

FWA = 1 + FWA = 0.9131524

FWA = FWA * &0476 = -0.6680554


Increment &49 -> now &49 = 3

Bit 0 of &49 is set, so adjust the calculated value as follows:

FWA = FWA * FWA = 0.446298
FWA = 1 - FWA = 0.5537019
FWA = SQR(FWA) = 0.7441115

Bit 1 of &49 is set, so FWA = - FWA = -0.7441115



Example 6: COS(5.63)

Set &046C = 5.63 (the argument)

Set FWB to 0.785398165 (PI/4)

FWA = FWA + FWB = 6.415398

FWA = FWA * 0.636619772 = 4.0841688

?&49 = 4 (Integer part of FWA value). FWA = IWA = 4

Store the FWA in &0471

FWA = FWA * -1.57080078 = -6.2832028

FWA = FWA + &046C = -0.6532028

Store FWA in &046C

Store &0471 (4) in FWA

FWA = FWA * 0.0000044544511 = 0.0000178

FWA = FWA + &046C = -0.6531849

[&A990] Store the FWA in &0476

FWA = FWA * FWA = 0.4266506

[&A861]: FWA = 1 / FWA = 2.3438381

Store FWA in &046C

FWA = FWA + -0.0119090311 = 2.3319291

FWA = 0.000107499 / FWA = 0.000046

FWA = -0.017164024 + FWA = -0.0171179

FWA = &046C + FWA = 2.3267202

FWA = 0.0013095369 / FWA = 0.0056282

FWA = 0.04999999 + FWA = 0.0556281

FWA = &046C + FWA = 2.3994662

FWA = -0.16666666 / FWA = -0.0694598

FWA = 1 + FWA = 0.9305401

FWA = FWA * &0476 = -0.6078147


Increment &49 -> now &49 = 5

Bit 0 of &49 is set, so adjust the calculated value as follows:

FWA = FWA * FWA = 0.3694387
FWA = 1 - FWA = 0.6305612
FWA = SQR(FWA) = 0.7940788

Bit 1 of &49 is not set, so exit with FWA unchanged


Example 7: COS(90)

Set &046C = 90 (the argument)

Set FWB to 0.785398165 (PI/4)

FWA = FWA + FWB = 90.785398

FWA = FWA * 0.636619772 = 57.795773

?&49 = 57 (Integer part of FWA value). FWA = IWA = 57

Store the FWA in &0471

FWA = FWA * -1.57080078 = -89.53564

FWA = FWA + &046C = 0.4643601

Store FWA in &046C

Store &0471 (57) in FWA

FWA = FWA * 0.0000044544511 = 0.0002539

FWA = FWA + &046C = 0.464614

Store the FWA in &0476

FWA = FWA * FWA = 0.2158661

[&A861]: FWA = 1 / FWA = 4.6324998

Store FWA in &046C

FWA = FWA + -0.0119090311 = 4.6205908

FWA = 0.000107499 / FWA = 0.0000023

FWA = -0.017164024 + FWA = -0.0171616

FWA = &046C + FWA = 4.6153381

FWA = 0.0013095369 / FWA = 0.0002837

FWA = 0.04999999 + FWA = 0.0502836

FWA = &046C + FWA = 4.6827834

FWA = -0.16666666 / FWA = -0.0355913

FWA = 1 + FWA = 0.9644086

FWA = FWA * &0476 = 0.4480777


Increment &49 -> now &49 = 58 (0011 1010)

Bit 0 of &49 is clear and Bit 1 of &49 is set, indicating that the FWA value needs to be complemented - so exit with FWA = - FWA = -0.4480777



Kranser 22:59, 2 October 2007 (BST)