Skip to content

Instantly share code, notes, and snippets.

@glossawy
Last active September 18, 2016 05:34
Show Gist options
  • Save glossawy/b7b69253d341a840842614ae31e77f3a to your computer and use it in GitHub Desktop.
Save glossawy/b7b69253d341a840842614ae31e77f3a to your computer and use it in GitHub Desktop.
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