Welcome to Dream.In.Code
Become a VB.NET Expert!

Join 150,085 VB.NET Programmers for FREE! Get instant access to thousands of VB.NET experts, tutorials, code snippets, and more! There are 1,800 people online right now. Registration is fast and FREE... Join Now!




Precision problem - math, VB2008EE

 
Reply to this topicStart new topic

Precision problem - math, VB2008EE, I want better accuracy

jens
10 May, 2008 - 10:08 AM
Post #1

D.I.C Head
Group Icon

Joined: 9 May, 2008
Posts: 185



Thanked: 11 times
Dream Kudos: 225
My Contributions
Hi!

Below is the code for a complete test of a function I wrote (I don't know how to send the form - it's just 6 textboxes and a button, see picture).

Problem is that when I run the program with data according to the picture the last textbox should be 0 - exactly. Now, since I don't have pi exact and alfa1 (see code) isn't exact either the sin function/procedure/method/whatever doesn't return 0 but somthing like 2E-14 and that bugs me. I guess it has somthing to do with the precision of a double and so on.

Is there a better way to solve this problem than to round small errors (see end of function)?

Regards
Jens

Attached Image

CODE

Imports System.Math

Public Class Form1

    Public Structure xyzType
        Dim x As Double
        Dim y As Double
        Dim z As Double
    End Structure


    '/==================================================================
    '/ function rotateKoordinates(axis, rotation, xyz) as xyzType
    '/ Returns (0, 0, 0) when stuff goes wrong.
    '/
    '/ 20080509 Jens
    '/ This function returns the coordinates of an object that has
    '/ been rotated around one axis in a coordinate system.
    '/ The function assumes that you rotate the object counter clockwise
    '/ as viewed from the positive end of the axis you rotate around.
    '/ This function is for VB.NET and it does need one structure to be
    '/ declared:
    '/ Public Structure xyzType
    '/   Dim x As Double
    '/   Dim y As Double
    '/   Dim z As Double
    '/ End Structure
    '/
    '/ You provide information about what axis you want to rotate around,
    '/ how many degrees you want to rotate and the x-, y- and z-coordinates before
    '/ the rotation.
    '/ The function returns the new coordinates for the object.
    '/ This can also be used if you let the object be fixed and
    '/ instead rotate the coordinate system clockwise around the provided axis.
    '/
    '/ Example; A vector (0, 3, 0) rotates 90 degrees counter clockwise
    '/ around the z-axis as seen from the positive end of the z-axis:
    '/ (z, 90, 0, 3, 0) => (-3, 0, 0)
    '/------------------------------------------------------------------
    Public Function rotateKoordinates(ByVal axis As Char, ByVal rotation As Double, ByVal xyz0 As xyzType) As xyzType
        Dim xyz1 As xyzType
        Dim pi As Decimal : pi = 3.1415926535897932384626433833D
        Dim radianer As Decimal, length As Decimal
        Dim alfa1 As Decimal

        xyz1.x = 0
        xyz1.y = 0
        xyz1.z = 0

        'In case somthing goes wrong at least return (0, 0, 0)
        'This is questionable, but a philosophical question.
        rotateKoordinates = xyz1

        'If no rotation or any whole number of complete
        'rotations, don't waste time computing the
        'obvious. Return what was sent to the function.
        If (rotation Mod 360) = 0 Then
            rotateKoordinates = xyz0
            Exit Function
        End If

        'Calculate radians from degrees since
        'the trigonometric functions use radians.
        radianer = CDec(rotation * (pi / 180D))

        Select Case axis
            Case CChar("x"), CChar("X")
                'Since we are rotating around the x-axis
                'the x-coordinate won't change.
                xyz1.x = xyz0.x

                'Calculate the length of the vector from origo to the coordinates given
                'NB: We only calculate the length in the yz-plane since we don't
                'do anything with the x-coordinate
                length = CDec(Sqrt(xyz0.y ^ 2D + xyz0.z ^ 2D))

                'IF y=0 we are on the z-axis, i.e: alfa0 = pi/2 eller alfa0=pi*3/2
                'If z=0 too won't be a problem
                If xyz0.y = 0 Then 'On the z-axis
                    alfa1 = CDec(IIf(xyz0.z > 0, (radianer + pi / 2D), (pi * (3D / 2D) + radianer)))
                Else
                    alfa1 = CDec(Atan(xyz0.z / xyz0.y) + radianer)
                End If
                xyz1.y = length * Cos(alfa1)
                xyz1.z = length * Sin(alfa1)
            Case CChar("y"), CChar("Y")
                xyz1.y = xyz0.y
                length = CDec(Sqrt(xyz0.x ^ 2D + xyz0.z ^ 2D))
                If xyz0.z = 0 Then ' On the x-axis
                    alfa1 = CDec(IIf(xyz0.x > 0, radianer + pi / 2D, pi * 3D / 2D + radianer))
                Else
                    alfa1 = CDec(Atan(xyz0.x / xyz0.z) + radianer)
                End If
                xyz1.z = length * Cos(alfa1)
                xyz1.x = length * Sin(alfa1)
            Case CChar("z"), CChar("Z")
                xyz1.z = xyz0.z
                length = CDec(Sqrt(xyz0.x ^ 2D + xyz0.y ^ 2D))
                If xyz0.x = 0 Then 'On the y-axis
                    alfa1 = CDec(IIf(xyz0.y > 0, radianer + pi / 2D, pi * 3D / 2D + radianer))
                Else
                    alfa1 = CDec(Atan(xyz0.y / xyz0.x) + radianer)
                End If
                xyz1.x = length * Cos(alfa1)
                xyz1.y = length * Sin(alfa1)
                'MsgBox(Sin(alfa1).ToString)
            Case Else
                Err.Clear()
                Err.Number = 5
                Err.Description = "You can only rotate around x, y or z -axis"
                Err.Raise(5)
                Exit Function
        End Select
        'Code below to ensure that I get exactly 0 when sin(alfa1) or cos(alfa1)
        'doesn't return 0 even thou alfa1 should be pi / 2 or pi / 4
        'If Abs(xyz1.x) < 10 ^ -12 Then xyz1.x = 0
        'If Abs(xyz1.y) < 10 ^ -12 Then xyz1.y = 0
        'If Abs(xyz1.z) < 10 ^ -12 Then xyz1.z = 0
        rotateKoordinates = xyz1
        Exit Function
    End Function

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        TextBox1.Text = "z"
        TextBox2.Text = "90"
        TextBox3.Text = "0"
        TextBox4.Text = "3"
        TextBox5.Text = "0"
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim xyz0 As xyzType, xyz1 As xyzType
        xyz0.x = CDbl(TextBox3.Text.Trim)
        xyz0.y = CDbl(TextBox4.Text.Trim)
        xyz0.z = CDbl(TextBox5.Text.Trim)

        xyz1 = rotateKoordinates(CChar(TextBox1.Text.Trim), CDbl(TextBox2.Text.Trim), xyz0)
        TextBox6.Text = xyz1.x.ToString & ", " & xyz1.y.ToString & ", " & xyz1.z.ToString
    End Sub

End Class


User is offlineProfile CardPM
+Quote Post

Martyr2
RE: Precision Problem - Math, VB2008EE
10 May, 2008 - 11:55 AM
Post #2

Programming Theoretician
Group Icon

Joined: 18 Apr, 2007
Posts: 5,660



Thanked: 313 times
Expert In: C/C++, Java, VB, VB.NET, C#, PHP, Web Development, HTML & CSS, Javascript

My Contributions
Well my guess is that you are an old vb6 programmer because a lot of the methods you are using here are old VB6 syntax. For instance you no longer store the answer in the name and exit the function. You use the return statement like other languages. Also functions like Asin, sin, cos etc are tied to a static object called "Math" and are called like Math.sin(value).

Third you are using some of your values as Decimal and some as Double. For best accuracy and best values you should always use the same data type as much as possible. This will reduce rounding errors and conversion errors from functions like CDec or CDbl.

As for errors, you don't use the error object like that anymore. .NET has exception handling for throwing errors instead of raising them. So check that out as well.

Your structure should be public members of type decimal just to coincide with the other decimals you are using (to help keep all the types the same for your calculations).

I can get your example down to a small value but based on the calculations I don't know if it is suppose to be exactly zero.

Fix it up a bit and see what happens. smile.gif
User is offlineProfile CardPM
+Quote Post

jens
RE: Precision Problem - Math, VB2008EE
10 May, 2008 - 01:29 PM
Post #3

D.I.C Head
Group Icon

Joined: 9 May, 2008
Posts: 185



Thanked: 11 times
Dream Kudos: 225
My Contributions
Hi, thank you for your time!

A few objections - just so we can strike them from the list... smile.gif

1) I thought that since I do "Imports System.Math" at the very top of the file I wouldn't have to write Math.Sin(value) but could write Sin(value). Is this wrong?

2) Errors: I'm aware of this... Ehemmm... Is there a Tutor or FAQ on exceptions?

3) Old VB6 programmer smile.gif, well, actually I'm an old Pascal programmer and brand new both to VB6 and VB2008... But I'll try to remember the return thing. Would it be somthing along the line of:
CODE

                xyz1.x = length * Cos(alfa1)
                xyz1.y = length * Sin(alfa1)
            Case Else
                'Do exception stuff here
                Exit Function
        End Select
        return xyz1
      End Function


4) With
CODE

   xyz0.x = 0
   xyz0.y = 3
   xyz0.z = 0
   rotateKoordinates(CChar("z"), CDbl("90"), xyz0)

the result should be exactly (-3, 0, 0)

5) The big one...
Since the function is thought to be used as a "black box", the internal representation of the values shouldn't matter as long as they are as good as - or better then - the external representation, don't you agree?
If you agree I don't understand what you mean. I believe I have all local variables as decimals - which I think has superior precision over doubles - while externals are doubles. This should prevent loss of precision to some extent I think.

Lets look at i.e:
CODE

   length = CDec(Sqrt(xyz0.y ^ 2D + xyz0.z ^ 2D))

Since length is a Decimal I have to cast the expression to Decimal.
I can't do anything about xyz0.y being defined as Double - except for defining it as a Decimal and force the users of this function to do unnessecary casts. The accuracy of xyz isn't cruical except for those special occasions when either should be exactly 0, and to represent 0 you don't need anything better than an double or even an integer.

Since I don't know the inner workings of the "^" operator and the exponent of an exponential expression is cruical for the result I force the number 2 to be a Decimal - bad idea?

Would you please elaborate a little on this part about the different representations and their results on precision?

Again, thanks for your time and please excuse if I seem a little stubborn, I just don't get part of what you're saying.

Regards
Jens
User is offlineProfile CardPM
+Quote Post

Fast ReplyReply to this topicStart new topic
Time is now: 1/8/09 11:41PM

Be Social

Dream.In.Code RSS Feed Dream.In.Code LinkedIn Group Follow Us On Twitter

Live VB.NET Help!

VB.NET Tutorials

Reference Sheets

VB.NET Snippets

DIC Chatroom

Bye Bye Ads

Monthly Drawing

Thumb Drive

Top Contributors

Top 10 Kudos This Month