Lincoln Doney
Lincoln Doney

Reputation: 86

Math.Sin giving me weird values

I'm trying to create a radial FAB menu, and to define the positions for all of the circles I have an algorithm which in theory works just fine, however when I actually run it, the Y value gives me something strange.. I'll post my code real quick

NOTE THAT THIS PART DOESN'T REALLY MATTER UNLESS YOU NEED REFERENCE AS TO WHAT A VALUE MEANS

    public class FABContainer : AbsoluteLayout
    {
        public static int NUMBER_FOR_RING = 1;
        public static double ANGLE_INCREMENT = 360.0 / ((double)NUMBER_FOR_RING);
        public static double CENTER_RADIUS = 100;
        public static double WIDTH_MOD = 0.9;
        public FABContainer()
        {
            this.BackgroundColor = Color.Transparent;
            FAB fab = new FAB();
            List<InternalFAB> internals = new List<InternalFAB>();
            internals.Add(new InternalFAB(this, internals.Count)
            {
            });
            this.Children.Add(fab);
            foreach(InternalFAB f in internals) { this.Children.Add(f); };
        }
    }
    public partial class FAB : SuaveControls.Views.FloatingActionButton
    {
        public FAB()
        {
            this.HeightRequest = FABContainer.CENTER_RADIUS;
            this.WidthRequest = FABContainer.CENTER_RADIUS * FABContainer.WIDTH_MOD;
            Debug.WriteLine($"CREATING NEW CENTER WITH W: {this.WidthRequest} H: {this.HeightRequest}");
                this.Image = "Edit_Icon.png";
            this.BackgroundColor = Color.Transparent;
            this.CornerRadius = (int)this.HeightRequest;
        }
    }
    internal class InternalFAB : SuaveControls.Views.FloatingActionButton
    {
        public static double DIST = 5;
        public EventHandler OnClicked;
        public static double radius = 80;
        public InternalFAB(FABContainer container, int count)
        {
            this.HeightRequest = (radius);
            this.WidthRequest = (radius * FABContainer.WIDTH_MOD * 0.95);
            this.BackgroundColor = Color.Transparent;
            int index = count + 1;
            double AngleDistortion = ((double)index) * FABContainer.ANGLE_INCREMENT;

            double xpos = (DIST + this.WidthRequest) * (Math.Cos((Math.PI / 180.0) * AngleDistortion));
            double ypos = (DIST + this.HeightRequest) * (Math.Sin((Math.PI / 180.0) *AngleDistortion));

            Debug.WriteLine($"CREATING NEW INTERNAL AT: {xpos},{ypos} from distortion {AngleDistortion}");

            this.TranslationX = xpos;
            this.TranslationY = ypos;
        }
    }

By the way it shouldn't matter but this is using Xamarin. Essentially the part that actually matters and is giving me a problem is this part:

double AngleDistortion = ((double)index) * FABContainer.ANGLE_INCREMENT;

double xpos = (DIST + this.WidthRequest) * (Math.Cos((Math.PI / 180.0) * AngleDistortion));
double ypos = (DIST + this.HeightRequest) * (Math.Sin((Math.PI / 180.0) *AngleDistortion));

Debug.WriteLine($"CREATING NEW INTERNAL AT: {xpos},{ypos} from distortion {AngleDistortion}");

For whatever reason, the Y value is returning as -2.08183080149804E-14 rather than the 0 that it is supposed to return in a "perfect" case. I ran through my code extensively to try to find the error to no avail.

If you need any more clarifications about my code, I'll tell you them right away.

Upvotes: 0

Views: 191

Answers (2)

user555045
user555045

Reputation: 64904

The multiplication by 85 is not important to this issue, so I will leave that aside and just look at (effectively) Math.Sin(Math.PI * 2). The result of that is -2.44921270764475E-16 on my machine.

That floating point arithmetic is not exact plays a role of course, but the result is not some arbitrary nonsense. What happens here is that in close proximity to x = 2pi, sin(x)≈x - 2pi. However, Math.Sin(Math.PI * 2) does not evaluate the sine at precisely 2pi, it cannot, 2pi is transcendental and definitely not representable as a floating point number. The input to the sine is already slightly "off", and has some value v=fl(2pi) where fl rounds a value to the nearest representable floating point number. Then sin(v) ≈ v - 2pi, because v is near 2pi.

So the result of Math.Sin(Math.PI * 2) should approximately represent how far the 2 pi that we passed in as argument is off from the real 2 pi. Math.PI * 2 has an exact value of 6.28318530717958623199592693708837032318115234375 on my machine, according to Wolfram Alpha that is -2.4492935982947063544521318645500... × 10^-16 off from the real 2 pi, which is pretty close to that -2.44921270764475E-16 value from earlier. It's not exactly there, but close enough to make the point that it's not just something arbitrary.

Upvotes: 0

Rich_Rich
Rich_Rich

Reputation: 439

You are working with doubles, performing floating point arithmetic. Meaning everything you do is approximated. If you want exact values, maybe C# isn't your goto language. Mathematica has support for these kinds of values.

Upvotes: 4

Related Questions