Why is Java giving me unusual results to basic arithmetic with doubles? - java

I'm writing some intelligence for a virtual rover driving around on Mars picking up resources. I have the following code:
public Point getPointFromRoverOffset(double offsetX, double offsetY) {
double x = offsetX + currentLocation.x;
double y = offsetY + currentLocation.y;
if(x > getWorldWidth()) {
x = x - getWorldWidth();
}
else if (x < 0) {
x = getWorldWidth() + x;
}
if(y > getWorldHeight()) {
y = y - getWorldHeight();
}
else if(y < 0) {
y = getWorldHeight() + y;
}
getLog().info("Based on location " + currentLocation.toString());
getLog().info("Decided that offset (" + offsetX + "," + offsetY + ") = (" + x + "," + y + ")");
return new Point(x, y);
}
All the numbers involved are doubles, representing a 2d vector in a 2d plane.
getWorldWidth() and getWorldHeight() both return 20.0
Im getting the following strange results:
[INFO] 16:41 Versatile - Based on location (0.0,6.0)
[INFO] 16:41 Versatile - Decided that offset (0.0,-5.999999999999999) = (0.0,8.881784197001252E-16)
Seemingly the input Y value -5.9 (recurring, a double rounding fault), and the current Y position 0.6 so the value should have been 0.1. Yet it comes out as 8.88 (et al).
Why? Is this some odd behaviour of doubles that I'm not aware of? Or am I missing something more obvious?

8.881784197001252E-16 is a very small number. it is approx 0.000000000000000888 or 8.88*10^-16. It is the difference between the value with a slight rounding error and the expect value.
If you need values to be exact, I suggest either
round the doubles
use a small allowed error in your calculations e.g. ERR = 1e-6
use integers (by scaling everything by 1000 for example)
use BigDecimal.

Related

How to prevent rounding to zero in Java [duplicate]

This question already has answers here:
Int division: Why is the result of 1/3 == 0?
(19 answers)
Closed 3 years ago.
I'm working on a Processing sketch that emulates the windmill animation shown in this video: 3Blue1Brown Windmill Problem However I'm having an issue where my float values get rounded to zero when they shouldn't. For example, I could have the line:float ratio= (520-581)/(158-87) this should give the result of -0.859 but instead it just gives 0.0. I know floats generally have a certain amount of inaccuracy due to the nature of how they work, but this seems extreme. What am I doing wrong, and how can I fix it?
Here's the full code snippet for those who are interested:
void detectCollision(ArrayList<Point> points){
for(Point point : points){
int x1 = rotationalPoint[0];
int y1 = rotationalPoint[1];
int x2 = point.get()[0];
int y2 = point.get()[1];
//skips the point if it's the same as the pivot point
if(x2 != x1 && y2 != y1){
//calculate the angle from the pivot point to a given point
float theta = atan((y2-y1)/(x2-x1));
println("Theta Before: " + degrees(theta));
/* These two lines compensate for the fact that atan as a range from PI/2 to -PI/2
they give the atan function a range from 0 to 2PI
*/
if(x2-x1 < 0) theta += PI;
if(x2-x1 > 0 && y2-y1 < 0) theta = 2*PI-abs(theta);
//some lines to help debug
println("P1: " + x1 + ", " + y1 + " P2: " + x2 + ", " + y2);
println("Theta: " + degrees(theta) + " Angle: " + degrees(angle));
/*checks to see if the current line's angle is close to the angle from the pivot point to the given point
if it is then it will make the given point the new pivot point
*/
if(angle<theta+rotationRate/2 && angle > theta-rotationRate/2){
this.rotationalPoint[0] = x2;
this.rotationalPoint[1] = y2;
}
}
}
}
Thank you for your help!
The division is taking Integer values as parameters, and because of that, it performs an 'integer division' with no floating point.
Parse your values as float before doing the division:
float ratio = (float)(520-581) / (float)(158-87);
System.out.println(ratio);
-0.85915494
Good luck!

How to calculate points on a line at intervals

I am coding a game and want a projectile to go from one location to the next moving at intervals every frame.
I've been playing around with the slope-intercept method of determining things and I'm getting close, but I am stuck.
Here is my code so far:
animationFrame = refresh;
double x, y, xPerF; //Values for drawing
double m, b; //Value for slope and y-intercept
double x1, x2, y1, y2; //Values for the targets
x1 = getCenterX();
x2 = Canvas.target[shotTarget].getCenterX();
y1 = getCenterY();
y2 = Canvas.target[shotTarget].getCenterY();
xPerF = Point2D.distance(x1, y1, x2, y2)/animationSpeed;
//Calculate slope
if(x2>x1) m = (y2-y1)/(x2-x1);
else if(x2<x1) m = (y1-y2)/(x1-x2);
else m = 0;
//Calculate the y-intercept
b = m * x1 - y1;
if(b<0) b = -b + Canvas.myHeight;
else {
b -= Canvas.myHeight;
if(b<0) b = -b;
}
//Calculate the x value
if(x1>x2) x = x1 - (xPerF * animationFrame);
else if(x1<x2) x = x1 + (xPerF * animationFrame);
else x = x1;
//Calculate the y value
if(m!=0) y = (m * x + b) - Canvas.myHeight;
else {
if(y1>y2) y = y1 - (xPerF * animationFrame);
else y = y1 + (xPerF * animationFrame);
}
g.fillOval((int) x - 15, (int) y - 15, 30, 30);
//Debugging
System.out.println("Frame " + animationFrame + " of " + animationSpeed + " | " + y + " = " + m + " * " + x + " + " + b + " | at speed of " + xPerF);
Updated
I expect the animation to end at the target location, but it always either overshoots or is right on target. It mainly overshoots when the target is pretty kind of straight above the tower, give or take a few x co-ordinates. I have worked this out to be a quadrant 1 x-y plane and I believe the problem I have now lies with how I am calculating my slope. Thanks!
Outdated
Here is a mini applet to demonstrate: https://drive.google.com/file/d/1fCTFJzulY1fcBUmdV6AXOd7Ol1g9B3lo/view?usp=sharing
Click on each target to target it
I believe your approach is fundamentally flawed. It is prone to rounding errors which might be a source for overshoots. It is also hard to make work well under real world where your application is not the only one so CPU might be in high demand and some frames might be skipped, and so on. The better approach is to use time rather than frames as the main driver of the events. Your main method drawScene accepts current time as one of its arguments. When any animation starts, you save the time when it started. Then, the job becomes much easier. For example for linear animation it would be something like this:
double animationPart = (currentTime - animationStartTime) / totalAnimationDuration;
// fix rounding error
if (animationPart > 1.0)
animationPart = 1.0;
double xCur = xStart * (1.0 - animationPart) + xEnd * animationPart;
double yCur = yStart * (1.0 - animationPart) + yEnd * animationPart;
P.S. the "time" doesn't have to be real time, it might be some other "game time" if it somehow makes more sense but still this approach is IMHO much easier to implement correctly.
Update
I'd say that the overall code quality is rather bad. Concerns are badly separated, there are a lot of magic numbers, and global static things. For example, having to two bullets in flight will be not easy to implement in this code.
There are also some real bugs in animation code. Some obvious bugs are:
xPerF is calculated wrongly. You divide the Euclidean distance instead of just difference in the X-coordinate.
Logic for the y is flawed. At least you should add m * (x - x1) instead of m * x. But it still won't cover the case of a vertical shoot (i.e. the case when the X-coordinate is not changed at all). If you want to go this way, you should use xPerF and yPerF and get rid of the m and the related if's.
This might or might not fix the animation issues (at list you still will have a potential for rounding errors). I'd still say that changing your shoot to something like
public void shootTarget(int target) {
shotTarget = target;
shotTime = animationFrame;
}
and then using
double animationPart = ((double) (animationFrame - shotTime)) / animationSpeed;
as suggested above is a better way. Note: this is only a stub because in your real code you for some reason regularly assign 0 to refresh and thus to animationFrame so it won't work that easy.
Answer
I figured it out. Instead of calculating the coordinates with slope-intercept method, I simply calculated the intervals I would have to increment y and x per frame and incremented them based on the frame of the animation.
double x, y, xPerF, yPerF; //Values for drawing
double x1, x2, y1, y2; //Values for the targets
x1 = getCenterX();
x2 = Canvas.target[shotTarget].getCenterX();
y1 = getCenterY();
y2 = Canvas.target[shotTarget].getCenterY();
xPerF = (Math.max(x1, x2) - Math.min(x1, x2))/animationSpeed;
yPerF = (Math.max(y1, y2) - Math.min(y1, y2))/animationSpeed;
if(x1>x2) x = x1 - xPerF * animationFrame;
else if(x1<x2) x = x1 + xPerF * animationFrame;
else x = x1;
if(y1>y2) y = y1 - yPerF * animationFrame;
else if(y1<y2) y = y1 + yPerF * animationFrame;
else y = y1;
g.fillOval((int) x - 15, (int) y - 15, 30, 30);

I am trying to calculate sine of an angle without using the Math.sin() in java

I am trying to calculate sine of an angle without using the Math.sin(). I got stuck in it's equation as I keep getting the wrong results
note I have a method that changes the angle from degrees to radians
public static double sin(double x, int precision) {
//this method is simply the sine function
double answer = 1, power = 1;
int n = 2,factorial = 1;
while (n<=precision) {
power = (power * x * x *-1) +1 ;
factorial = (factorial * (n +1))* (n-1);
answer = answer + ((power/factorial ));
n = n + 2;
}
return answer;
}
It looks like you're attempting to calculate the sine of angle given in radians using the Maclaurin series, a special case of Taylor series.
sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ...
Your initial answer is 1 when it should be x. Your initial power is 1 when it should be x also.
double answer = x, power = x;
For some reason you're adding one to the power part of the result when you shouldn't be.
power = (power * x * x * -1);
You'll also need to fix your factorial calculation. Multiply by n + 1 and n, not n + 1 and n - 1.
factorial = (factorial * (n + 1)) * (n);
With these fixes, testing:
for (double angle = 0; angle <= Math.PI; angle += Math.PI / 4)
{
System.out.println("sin(" + angle + ") = " + sin(angle, 10));
}
The results are pretty good considering the limitations of precision for floating point arithmetic.
sin(0.0) = 0.0
sin(0.7853981633974483) = 0.7071067811796194
sin(1.5707963267948966) = 0.999999943741051
sin(2.356194490192345) = 0.7070959900908971
sin(3.141592653589793) = -4.4516023820965686E-4
Note that this will get more inaccurate as the values of x get larger, not just because of the inaccuracy to represent pi, but also because of the floating point calculations for adding and subtracting large values.

strange thing with cylinder algorithm

I want to render a cylinder in Opengl. For that i wrote an simple algorithm, that
generates me the points mesh by the parameters radius, height, xSubDivisions and ySubDivisions:
(Java)
for(int yDivision = 0; yDivision < yDivisionCount; yDivision++){
for(int xDivision = 0; xDivision < xDivisionCount; xDivision++){
float line[] = getVboLine(xDivision, yDivision, radius, height, xDivisionCount, yDivisionCount);
string.append(line[0] + ", " + line[1] + ", " + line[2] + ", " + line[3] + ", " + line[4] + ", ");
}
}
public float[] getVboLine(int xDivision, int yDivision, float radius, float height, int xDivisionCount, int yDivisionCount){
float xDegrees = 360.0f / xDivisionCount * xDivision;
float xRadian = (float) Math.toRadians(xDegrees);
float x = (float) Math.sin(xRadian) * radius;
float z = (float) Math.cos(xRadian) * radius;
float y = (float) yDivision * (height / (yDivisionCount - 1));
float s = xDegrees * (1.0f / 360.0f);
float t = yDivision * (1.0f / (yDivisionCount - 1));
return new float[]{
x, y, z, s, t
};
}
The result is actually an cylinder, (i created an IBO to render this points) but sometimes, with different inputs for x and yDivisions there is a strange gap in it.
I couldn't find a rule, but the values i found this bug with were 200, 100.
To debug i rendered only the points. The result was:
How is this possible? One points is just missing (where i added the reed circle with paint).
Where is the problem with my algorithm?
I am not JAVA coder but you are mixing int and float together
for example:
xDegrees = 360.0f / xDivisionCount * xDivision
[float] [float] [int] [int]
I would rather use this:
xDegrees = float(360*xDivision)/float(xDivisionCount)
multiplication should go always first (if operands are >= 1)
and division after that to preserve accuracy
some weird rounding could cause your problem but it would be more noticeable for lower xDivisionCount not bigger one
Bug breakpoint
add to your code last generated point
after new point computation compute the distance from last point
add if (|distance-some_avg_distance|>1e-10)
and add breakpoint inside
some_avg_distance set by distance that should be there (get it from trace)
this way you can breakpoint the point causing problems (or the next point to it)
so you can actually see what is wrong
my bet is that by rounding you get the same angle as prev/next point
and therefore you do not have missing point but some duplicate instead
you can check that also by Blending

How to get the size of the intersecting part in a circle in Java

I need the size of the black part of this image:
I've done some research about how to find it in normal math, and I was pointed to this website: Website
The final answer on getting it was
(from MathWorld - A Wolfram Web Resource: wolfram.com)
where r is the radius of the first circle, R the radius of the second circle, and d the distance between the two centers.
The code I tried to use to get the size of this was the following:
float r = getRadius1();
float R = e.getRadius1();
float deltaX = Math.abs((getX() + getRadius()) - (e.getX() + e.getRadius()));
float deltaY = Math.abs((getY() + getRadius()) - (e.getY() + e.getRadius()));
float d = (float) Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
float part, part2, part3;
//Chopping it in parts, because it's easier.
part = (float) (Math.pow(r,2) * Math.acos(
Math.toRadians((Math.pow(d, 2) + Math.pow(r, 2) - Math.pow(R, 2))/(2*d*r))));
part2 = (float) (Math.pow(R,2) * Math.acos(
Math.toRadians((Math.pow(d, 2) + Math.pow(R, 2) - Math.pow(r, 2))/(2*d*R))));
part3 = (float) (0.5 * Math.sqrt((-d + r + R) * (d+r-R) * (d-r+R) * (d+r+R)));
float res = part + part2 - part3;
Main.log(res + " " + part + " " + part2 + " " + part3+ " "
+ r + " " + R + " " + d);
//logs the data and System.out's it
I did some testing, and the output was this:
1345.9663 621.6233 971.1231 246.78008 20.0 25.0 43.528286
So that indicates that the size of the overlapping part was bigger than the circle itself (which is r^2 * PI).
What did I do wrong?
Just a guess (as stated in my comment): try removing the Math.toRadians(...) conversion.
Since there are no degrees involved in the formula but rather radii, I assume the parameter to cos-1(...) is already a value in radians.
If I remove the conversion and run your code, I get the following overlap area size: 11.163887023925781 which seems plausible since the length of the overlap segment on the line between the two centers is 20 + 25 - 43.5 = 1.5 (approximated)
Edit:
If I set the distance to 5 (the smaller circle is completely contained in the bigger one but touches its edge) I get the overlap area size 1256.63 which is exactly the area of the smaller circle (202 * Π). The calculation doesn't seem to work if the distance is smaller than the difference of the radii (i.e. in your case smaller than 5), but that might just be a problem of numerical representation (the normal datatypes might not be able to represent some of the intermediate results).

Categories