Last active
September 18, 2016 05:34
-
-
Save glossawy/b7b69253d341a840842614ae31e77f3a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
# CS 1 does not encourage Global variables. I only use them for this example! | |
# even then I use conventions indicating private variables (_ or __) | |
# Ignore the operators and anything you may not know yet! The documentation is the important part! | |
# Very descriptive because bit operations are VERY obfuscated | |
""" | |
Descriptive comments for complex/unclear operations. | |
Determining how many different values of sine to pre-calculate. In essence, | |
__SIN_BITS represents how granular our measurements should be, and __SIN_MASK ensures that the | |
value is a power of two utilizing bit operations. | |
for simplicity we will consider a 16 bit number | |
-1 in 16 bit two's complement is 1111 1111 1111 1111 | |
-1 << 13 is 1110 0000 0000 0000 (left shift bits 13 positions) | |
~(-1 << 13) is 0001 1111 1111 1111 (invert bits, 0 becomes 1 and 1 becomes 0) | |
Adding 1 then yields a power of two: 0010 0000 0000 0000 | |
In this case it is 8192 in decimal, which is how many different values of Sine we will calculate | |
""" | |
__SIN_BITS = 13 | |
__SIN_MASK = ~(-1 << __SIN_BITS) | |
__SIN_COUNT = __SIN_MASK + 1 | |
# Simple comments for sections of unambiguous, clear code | |
# Number of radians and degrees in a full circle | |
__RADIANS_FULL = math.pi * 2 | |
__DEGREES_FULL = 360 | |
""" | |
Using Multi-line comments or single-line comments is preference-based. But for longer explanations | |
I tend to use multi-line comments over many one-line comments. FOLLOW THE STYLE GUIDE. | |
Number of Degrees belonging to each index in the list of sine values and the number of | |
radians belonging to each index in the list of sine values. | |
""" | |
__RADIANS_TO_INDEX = __SIN_COUNT / __RADIANS_FULL | |
__DEGREES_TO_INDEX = __SIN_COUNT / __DEGREES_FULL | |
# Radians/Degrees Conversion Constants | |
__RADIANS_TO_DEGREES = 180 / math.pi | |
__DEGREES_TO_RADIANS = math.pi / 180 | |
# Simple comments for anything clear to someone who is fairly familiar with python | |
# Initialize sine table to __SIN_COUNT length, full of None values. One way to extend a list. | |
__SIN = [None] * __SIN_COUNT | |
""" | |
For every possible sine value, take a point from the unit circle in radians and | |
calculate it's real sine value. the value should be such that any value on the interval | |
[i, i+1) will be that value. | |
This is not precise, but is accurate and fast. | |
""" | |
for i in range(0, __SIN_COUNT): | |
angle = (i + 0.5) / __SIN_COUNT * __RADIANS_FULL | |
__SIN[i] = math.sin(angle) | |
""" | |
Calculate precise values for common angles, 0, 90, 180, 270. | |
""" | |
for i in range(0, 360, 90): | |
# & __SIN_MASK is used to ensure the index falls in range, numbers are kept in the range [0, 8192) | |
__SIN[int(i * __DEGREES_TO_INDEX) & __SIN_MASK] = math.sin(i * __DEGREES_TO_RADIANS) | |
# Comment before the function would not be visible to someone using your API, but maintainers and other contributors will! | |
# We may need to make the sine table more granular... | |
def sin(x): | |
""" | |
Summarizing docstring, if you are curious the format is using the reStructuredText format | |
Takes a value in radians and returns an approximation of the result of taking sine of that value by retrieving | |
a pre-computed table of approximations. Values are also wrapped around, thus 2*pi + 1 is the same result as | |
1 and the result of -pi/2 is the same result as 3*pi/2. | |
It's worth noting, preconditions and postconditions are not always necessary. This function doesn't need it since | |
it is evident it takes a number. Though I could say: | |
:pre: x is a Number | |
I don't really have a post-condition. It's only when you change things outside of your function will you need a postcondition. | |
With turtle, we are modifying a global turtle state, which is why postconditions are important. | |
:param x: Angle in radians | |
:return: The approximate result of sin(x) | |
""" | |
return __SIN[int(x * __RADIANS_TO_INDEX) & __SIN_MASK] | |
def sin_degrees(x): | |
""" | |
Takes a value in degrees and returns an approximation of taking sine of that value by retrieving | |
form a pre-computed table of approximations. Values also wrap around, thus 361 is the same result as 1 | |
and -180 is the same result as 270. | |
:param x: Angle in degrees | |
:return: The approximate result of sin(x) | |
""" | |
return __SIN[int(x * __DEGREES_TO_INDEX) & __SIN_MASK] | |
# A quick and dirty (note: not all-encompassing) test comparing math.sin results to our approximations | |
# printing x, sin(x), and approx_sin(x) | |
print("Value\t\tNative\t\tTabular") | |
for i in range(0, __DEGREES_FULL): | |
print("{!s}\t\t{!s}\t\t{!s}".format(i, math.sin(i*__DEGREES_TO_RADIANS), sin_degrees(i))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment