0

Trying to solve Building Skills with Python, area of a flag . I am trying to solve for the blue area however I'm off by about 2%. Additionally when I total up the other areas, the total does not equal the area of the flag itself. My code is as follows:

import math


def area_star(width):
    a = 36.00
    b = 72.00

    radius_star = 0.0308 * width

    a_radians = float(a) * float(math.pi) / float(180)
    b_radians = float(b) * float(math.pi) / float(180)

    a_sin = math.sin(float(a_radians)/float(2))
    b_sin = math.sin(float(b_radians)/float(2))

    top = a_sin*b_sin

    c_radians = float((a+b)*math.pi)/float(180) 
    c_sin = math.sin(c_radians)

    bottom = 0.5 * c_sin    
    return 5 * float(top)/float(bottom) * radius_star * radius_star
def fifty_stars(width):
    return 50 * area_star(width)
def calculate_areas(width):
    WIDTH = width
    length = 1.9 * WIDTH
    width_union = float(7)/float(13) * WIDTH
    length_union = 0.76*WIDTH
    NUMBER_RED_STRIPES = 7
    NUMBER_WHITE_STRIPES = 6

    width_strip_denom = NUMBER_RED_STRIPES+NUMBER_WHITE_STRIPES
    width_strip = float(1)/float(width_strip_denom)*WIDTH

    blue_area = length_union * width_union - fifty_stars(WIDTH)
    white_area = 3 * width_strip * (length*length_union)+3*width_strip*length+fifty_stars(WIDTH)
    red_area = 4 * width_strip*(length*length_union)+3*width_strip*length




    print 'Our width was given as : %f' %WIDTH
    print 'Our length calculates as : %f' %length

    print 'Width of our union is: %f' %width_union
    print 'Length of our union is: %f' %length_union

    print 'Area of a star is %f'%area_star(WIDTH)
    print 'Area of 50 stars is %f'%fifty_stars(WIDTH)

    print 'Area of our flag in total is : %f '%(WIDTH*length)
    print 'Actual WHITE AREA is %f'%white_area
    print 'Actual RED AREA is %f'%red_area
    print 'Expected BLUE AREA is %f' %(WIDTH*length*.1873)
    print 'Actual BLUE AREA is %f'%blue_area

    print 'SumofallAreas: %f' % (red_area+white_area+blue_area)



calculate_areas(1.0)

My output is:

Our hoist was given as : 1.000000
Our length calculates as : 1.900000
Width of our union is: 0.538462
Length of our union is: 0.760000
Area of a star is 0.001812
Area of 50 stars is 0.090587
Area of our flag in total is : 1.900000 
Actual WHITE AREA is 0.792126
Actual RED AREA is 0.789231
Expected BLUE AREA is 0.355870
Actual BLUE AREA is 0.318644
SumofallAreas: 1.900000

Is there something about Float that could explain the variance or is the problem in my code itself?

*********UPDATED CODE PER SUGGESTS*****************

import math
from fractions import Fraction

def area_star(width):
    a = 36.00
    b = 72.00

    radius_star = 0.0308 * width

    a_radians = float(a) * float(math.pi) / float(180)
    b_radians = float(b) * float(math.pi) / float(180)

    a_sin = math.sin(float(a_radians)/float(2))
    b_sin = math.sin(float(b_radians)/float(2))

    top = a_sin*b_sin

    c_radians = float((a+b)*math.pi)/float(180) 
    c_sin = math.sin(c_radians)

    bottom = 0.5 * c_sin    
    return 5 * float(top)/float(bottom) * radius_star * radius_star

def fifty_stars(width):
    return 50 * area_star(width)

def calculate_areas(width):
    hoist = width
    fly = hoist * Fraction(19,10)

    jack_hoist = Fraction(7,13) * hoist 
    jack_fly = Fraction(76,100)*hoist

    NUMBER_RED_STRIPES = 7
    NUMBER_WHITE_STRIPES = 6

    width_strip_denom = NUMBER_RED_STRIPES+NUMBER_WHITE_STRIPES
    width_strip = Fraction(1,width_strip_denom)*hoist

    blue_area = jack_fly * jack_hoist - fifty_stars(hoist)
    white_area = 3 * width_strip * (fly-jack_fly)+3*width_strip*fly+fifty_stars(hoist)
    red_area = 4 * width_strip*(fly-jack_fly)+3*width_strip*fly




    print 'Our hoist was given as : %f' %hoist
    print 'Our length calculates as : %f' %fly

    print 'Width of our union is: %f' %jack_hoist
    print 'Length of our union is: %f' %jack_fly

    print 'Area of a star is %f'%area_star(hoist)
    print 'Area of 50 stars is %f'%fifty_stars(hoist)

    print 'Area of our flag in total is : %f '%(hoist*fly)
    print 'Actual WHITE AREA is %f'%white_area
    print 'Actual RED AREA is %f'%red_area
    print 'Expected BLUE AREA is %f' %(hoist*fly*.1873)
    print 'Actual BLUE AREA is %f'%blue_area

    print 'SumofallAreas: %f' % (red_area+white_area+blue_area)



calculate_areas(1.0)
Jeeves
  • 424
  • 1
  • 7
  • 25
  • Possible duplicate of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Aidan Jun 23 '16 at 04:23
  • It would be easier to audit if you used variables that matched the problem description. `Wf = 1.0`, for example. – Mark Tolonen Jun 23 '16 at 04:44
  • Updated variables, added Fraction function. Sum now totals to correct amount however individual BLUE_AREA is still off?? – Jeeves Jun 23 '16 at 05:04
  • 1
    Please avoid replacing your original code with revised versions! It makes existing answers irrelevant and the whole question page confusing. You can add a new version of your after the original, with an explanation that it is a revised version based on some given answers and/or comments. – taleinat Jun 23 '16 at 05:15
  • Most of the magic numbers can be derived. For example, the angle B of 72º is just 1/5 of a circle (because the star has 5 points), while the angle A is just half of B. The only required givens are the aspect ratio of the flag (1.9 fly:hoist), the fly of the canton (40% of the flag's fly), and the size of the stars (diameter = 80% the hoist of one stripe), along with the facts that there are 13 stripes, the top and bottom ones are red, and the canton spans the top 7. – Mark Reed Jun 23 '16 at 05:41
  • The two given computations of K don't come out the same. The second one gives the 18.73% blue answer. – Mark Tolonen Jun 23 '16 at 06:03
  • Yes the formulas yield diff results. – Jeeves Jun 23 '16 at 14:37

4 Answers4

2

Here's my calculation. The original problem had errors noted in comments below.

Note: The from __future__ makes division work like Python 3, where 1/2 = 0.5, not 0 like in Python 2. This cleans up the math.

Also, using same variables as the problem statement made it easier to enter and verify the formulas. I found the two versions of K didn't give the same answer, so worked the problem independently and found the golden ratio version of 5*K got the same answer I did for the area of a star.

from __future__ import division
from math import sin,pi

Wf = 1.0
Lf = 1.9 * Wf
A = Wf * Lf
Wu = 7/13 * Wf
Lu = .76 * Wf
R = .0308 * Wf
Sr = 7
Sw = 6
Ns = 50
Ws = 1/(Sr+Sw) * Wf

a = 36 * pi/180
b = 72 * pi/180
GR = (1 + 5**.5)/2
K = sin(b/2)/GR**2 * (R**2) # Golden ratio version of K was correct, other was wrong.
S = 5 * K

Red = 4 * Ws * (Lf - Lu) + 3 * Ws * Lf
White = 3 * Ws * (Lf - Lu) + 3 * Ws * Lf + Ns * S
Blue = (Lu * Wu) - Ns * S   # Error on problem page used (Lu - Wu)

print('Red   =',Red)
print('White =',White)
print('Blue  =',Blue)
print('total =',Red+White+Blue)
print('Red   = {:%}'.format(Red/A))
print('White = {:%}'.format(White/A))
print('Blue  = {:%}'.format(Blue/A))

Output:

Red   = 0.7892307692307692
White = 0.7547841990012687
Blue  = 0.355985031767962
total = 1.9
Red   = 41.538462%
White = 39.725484%
Blue  = 18.736054%
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Thanks - this worked. I was looking at the second formula before I called it a night and noticed they got different values. Modified my original to compensate for this variant formula. Not sure why they list two formulas that yield different results, unless different use cases. – Jeeves Jun 23 '16 at 14:36
  • @Jeeves I think it was just an error in the calculation, or maybe another typo like the Blue area formula using subtraction instead of multiplication. I didn't try to derive the first formula to determine the mistake. – Mark Tolonen Jun 23 '16 at 16:07
0

Floating point precision issues aren't causing your calculations to be so significantly off. You've just got bugs in your code. Specifically, from your output, Expected BLUE AREA is 0.355870 and Actual BLUE AREA is 0.318644 are caused by using significantly different calculations.

A potential problem I can identify with a quick look: Should both width_union and length_union be calculated relative to the flag's width?

You may have additional bugs, it is difficult to see with so many "magic numbers" littered throughout the code... (e.g. float(7)/float(13), 0.76).

taleinat
  • 8,441
  • 1
  • 30
  • 44
  • Not sure what "magic" numbers are--tried to make readable for assistance. Formulas are direct from the Link and as outlined. – Jeeves Jun 23 '16 at 04:41
  • "magic numbers" are numbers that show up without explanation in the code. They're generally better entered as constants at the top, like `AspectRatio = 1.9`, `StripeCount = 13`, `RedStripes = 7`, `StarRadius = 0.4 * StripeHoist`, etc. – Mark Reed Jun 23 '16 at 05:12
  • Normally I would put constants up top, but his was just an exercise-- seemed overdone for such. But now I know what 'magic numbers' are--and not to trade them for a cow! – Jeeves Jun 23 '16 at 14:37
0

First, it can be confusing to refer to 'length' and 'width' in this context; it would be better to use the vexillological terms 'hoist' (the dimension parallel to the flagpole) and 'fly' (the dimension perpendicular to the flagpole).

Second, your numbers are off. You're using length*length_union for the fly of a high stripe, but that's not correct. Besides the fact that length_union is not a proportion of the fly, but of the hoist, it's already multiplied out, so you shouldn't multiply it again. In any case, you don't want the fly of the canton, but that of the stripe, which is the total fly minus the canton.

I haven't checked the rest of your math, but if you use length-length_union instead of length*length_union you might get better results.

Ignoring the stars, you get these areas, if that helps you debug. Like in your code, 1.0 is the area of a square flag whose side is the same as the hoist; the whole flag therefore has area 1.9.

red = 513/650 (~ 0.789231)
white stripes only = 228/325 (~ 0.701538)
blue canton + white stars = 133/325 (~ 0.409231)
total = (513 + 228 * 2 + 133 * 2)/650 = 1235/650 = 1.9

Factoring in the stars will shift some of that from blue to white, but the red (and the total of all three) shouldn't change.

If you do want better precision, you could switch to using exact rational arithmetic with the fractions module:

from fractions import Fraction
hoist = 1
fly = hoist * Fraction(19,10)
stripe_hoist = hoist * Fraction(1,13)
canton_hoist = 7 * stripe_hoist
canton_fly = fly * Fraction(2, 5)

etc.

Mark Reed
  • 91,912
  • 16
  • 138
  • 175
  • Thanks for the suggestions- updated var names changed the length*length_union to length-lenght_union. Getting different sum total but the actual blue_area by itself still off?? – Jeeves Jun 23 '16 at 05:05
-3

Python is known to have issues dealing with floating point operations.

Here is more information for you Floating Point Arithmetic: Issues and Limitations

Edit: Also a previous Stack addressing the same/similar issue Python rounding error with float numbers [duplicate]

Community
  • 1
  • 1
Aidan
  • 757
  • 3
  • 13