I'm trying to use try/throw/catch to check the values of cos(theta) and sin(theta) as they are not quite 0 at theta=90 etc. I originally used the following code which worked fine (this is part of a class so some variables are defined outside the function):
After going through the rest of the code this gives the correct values for theta=90 and others.
However, to make this more elegant (and "advanced"), I'm trying to use try/throw/catch:
then the first cout on line 4 shows that the correct values should be being entered into the array but the cout at line 7 gives values like 1e-307 when it should be around 1.
I'm trying to use try/throw/catch to check the values of cos(theta) and sin(theta) as they are not quite 0 at theta=90 etc.
I am not so sure that is a good use for exception handling. Exception handling is for exceptional things: class invariant cannot be satisfied; file doesn't exist; network down; those kind of things.
Also not sure why you are concerned with values not being exactly 0.0 or 1.0 . If you are worried about equality, the normal way of doing that is use a precision value: see if the absolute value of the difference is less than it. You are already doing this in your try blocks, although you should make the precision a const variable, and use std::abs.
What is the concern over the values not being exact? std::cout does rounding.
The concern is that I'm rotating the vertices of shapes using a 2x2 rotation matrix and the slight (~1e-16) offset from not being quite 0 at 90 degrees etc has an effect unfortunately.
I know it's probably not the best use of exception handling but it's the best place for it in my project. This code is for an end of year project for a C++ module in uni and one of the marking criteria is to use "Advanced C++ Techniques" and one of the techniques is exception handling. It seemed like the easiest to implement without rewriting the entire code and my house mate has already done it but we can't figure out why this is not working.
The concern is that I'm rotating the vertices of shapes using a 2x2 rotation matrix and the slight (~1e-16) offset from not being quite 0 at 90 degrees etc has an effect unfortunately.
Can you explain how the effect shows itself? I could understand how "errors" might arise (eg 2 rotated points don't exactly coincide when they should), but these and other cases are normally covered by testing with the precision value.
I know it's probably not the best use of exception handling but it's the best place for it in my project.
Consider that you may loose more marks for using exceptions inappropriately. I would at least put them in your classes so that they look after class invariants properly.
It shows itself when the value of the co-ordinate should be 0 as (as you said) it rounds it if it's close to an integer number.
For example, I define a square with vertices:
v1 = (0,0)
V2 = (0,1)
v3 = (1,1)
v4 = (1,0)
and then rotate it through 90 degrees (without any catching) which gives:
v1 = (1,0)
v2 = (0,6.1e-17)
v3 = (1.1e-16,1)
v4 = (1,1)
This is because the x value is calculated by
xnew = xold*cosAng - yold*sinAng
and the y value is calculated by
ynew = xold*sinAng + yold*cosAng
Like I said it's a small effect but I'd rather make sure it's caught rather than hope no one happens to make a shape which rotates to a 0.
I don't know what class invariants are or how they are made/used.
Like I said it's a small effect but I'd rather make sure it's caught rather than hope no one happens to make a shape which rotates to a 0.
But it doesn't matter. It doesn't have any material effect. What's wrong with a point that has a zero ordinate?
I don't know what class invariants are or how they are made/used.
A class invariant is a member variable that must have a sensible value in order for the whole object to make sense. For example someone's age must be positive and less than some reasonable value (120 years say), distance between 2 points must be positive, 3 points for a triangle or circular arc must not be collinear within a precision value.
If an invariant is not satisfied, then there is no sense in creating the object. One can use use a function try block in the constructor initializer list to enforce problems with the constructor arguments, and throw from the constructor body for anything else which may not be valid.
If a member function call is going to result in an invariant not being satisfied, then that function can throw.
There is also the concepts of pre-conditions and post-conditions. pre-conditions are conditions on the function arguments, while post conditions pertain to return values or object altered by reference. It's possible for a pre-condition to be satisfied, but it still might result in violation of the invariant. 3 collinear points for a triangle is an example of that: the points themselves are valid, their relationship to each other is not.
A post condition might be the positive distance example. It can also be a precondition:
1 2 3 4
Point NewPoint(const Point& ExistingPt, constdouble Angle, constdouble Distance) {
// pre condition: 0.0 <= Angle < (2 * PI) radians
// pre condition: 0.0 < Distance
}
There are a number of places where one does have to be careful of values near zero (but all of them can be dealt with by comparison with a precision value), the most obvious is divide by zero.
Another is PointInCircle: it's easy to calc the distance from the point to the centre of the circle. If this distance is less then the radius, then it's inside. But what of a point which is deemed near enough to be on the circle? Same story: if (std::abs(Distance - Radius) < precision )
There are many of other examples: line intersections, area and distance comparisons and so forth.
In the original code: if ((angle == 0) || (angle == 360) || (angle == -360)){
Don't ever do this: there will be numbers that will not be represented exactly, so always avoid equality conditions - better to use relational comparisons.
Another pedantic note: I always put numbers on both sides of the decimal point for floating point literals; It reminds me that it is a double. So always 0.0 or 0.3 or 3.0 ,not 0 or 0. or .0 or .3 or 3 or 3.