How to prevent rounding to zero in Java [duplicate] - java

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!

Related

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.

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

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.

calculating shortest grid distance

Consider a city where the streets are perfectly laid out to form an infinite square grid. In this city finding the shortest path between two given points (an origin and a destination) is much easier than in other more complex cities. As a new Uber developer, you are tasked to create an algorithm that does this calculation.
Given user's departure and destination coordinates, each of them located on some street, find the length of the shortest route between them assuming that cars can only move along the streets. You are guaranteed that at least one of the coordinates is an integer.
I am struggling a little to figure out the logic here. There are many cases and I don't know how to accommodate them all. This is what I have so far
double perfectCity(double[] departure, double[] destination) {
double yDist = Math.abs(destination[1]-departure[1]);
double xDist = Math.abs(departure[1] - departure[0] + departure[1]-destination[0]);
return xDist + yDist;
}
The algorithm is very simple if the inputs are integers, just find the absolute value between the x and y coordinates and then add them together. This is called the Manhattan distance.
int distance = Math.abs(x1 - x2) + Math.abs(y1 - y2);
With doubles, it is almost exactly the same, except for one situation. Here are some possibilities:
Both points have integer coordinates
One point has integer coordinates, and the other point has only one integer coordinate
Both points have only one integer coordinate, but they are on different axes.
Both points have only one integer coordinate, and they are on the same axis.
Possibilities 1-3 all work fine using the same algorithm as for finding distance with integers, except #4 has the possibility of the axis in common being on the same block.
For example, if the inputs were: {x: 0.5, y: 2} and {x: 0.5, y: 3} you would have to travel horizontally, vertically, and then backwards horizontally again in order to reach the destination. This is different from inputs of {x: 0.5, y: 2} and {x: 1.5, y: 3} because there is no need to travel backwards on the same axis.
So you can use the normal algorithm in all cases except for the case of when both of the Xs or Ys have floating-point values and have the same floor-ed value.
Your code should look something like this.
import static java.lang.Math.*;
public static double perfectCity(double x1, double y1, double x2, double y2) {
double xDist = abs(x1 - x2);
double yDist = abs(y1 - y2);
if (floor(x1) != x1 && floor(x2) != x2 && // both Xs are doubles
floor(x1) == floor(x2) && // on the same block
y1 != y2) { // not on the same street
xDist = min(abs(x1 - floor(x1) + x2 - floor(x2)),
abs(x1 - ceil(x1) + x2 - ceil(x2)));
} else if (floor(y1) != y1 && floor(y2) != y2 && // both Ys are doubles
floor(y1) == floor(y2) && // on the same block
x1 != x2) { // not on the same street
yDist = min(abs(y1 - floor(y1) + y2 - floor(y2)),
abs(y1 - ceil(y1) + y2 - ceil(y2)));
}
return xDist + yDist;
}
This can be much further simplified by using a helper function to calculate each axis separately.
public static double perfectCity(double x1, double y1, double x2, double y2) {
return travelOnAxis(x1, x2, y1 == y2) + travelOnAxis(y1, y2, x1 == x2);
}
private static double travelOnAxis(double from, double to, boolean travelIsStraight) {
if (Math.floor(from) == Math.floor(to) && !travelIsStraight) {
double dist = Math.abs((from % 1) + (to % 1));
return Math.min(dist, 2 - dist);
} else {
return Math.abs(from - to);
}
}
I used the trick with 2 - dist here because it's the same as calculating
Math.abs((1 - (from % 1)) + (1 - (to % 1)))
which is the same as
Math.abs(from - Math.ceil(from) + to - Math.ceil(to))
If this is a square grid, you can consider the x and y coordinates separately; the minimum distance is the sum of the minumum distances in the two directions.
In the p-direction (either x or y), you have to move from p1 to p2. From p1, you can either move to floor(p1) or ceil(p1) to get to a road (which may be equal, if p1 is an integer); from there, you can move to either floor(p2) or ceil(p2), the road on which p2 is located; from there, you can move to p2.
So, the minimum distance in the p-direction is
min(abs(p1 - ceil(p1) ) + abs(ceil(p1) - floor(p2)) + abs(floor(p2) - p2), # (1)
abs(p1 - floor(p1)) + abs(floor(p1) - ceil(p2) ) + abs(ceil(p2) - p2), # (2)
abs(p1 - floor(p1)) + abs(floor(p1) - floor(p2)) + abs(floor(p2) - p2), # (3)
abs(p1 - ceil(p1) ) + abs(ceil(p1) - ceil(p2) ) + abs(ceil(p2) - p2)) # (4)
So you can just calculate this independently for the x and y directions, and add.
To illustrate this (abbreviating floor and ceil as f and p respectively):
f(p1) p1 c(p1)
+---O>>>>+>>>>>>>>+
.
.
+>>>O----+
f(p2) p2 c(p2)
--------------------------------> p axis
The shortest route is indicated here with >. The .s are on the shortest route, but since that part of the route is orthogonal to the p direction, it "doesn't count" towards the minimum distance in that direction.
The minimum route shown here, p1 -> c(p1) -> f(p2) -> p2, is Case 1 above.
It should not be hard to visualize swapping p1 and p2, in which case the minimum route is to go from p1 ->f(p1) -> c(p2) -> p2 (Case 2).
The case of pN == f(pN) == c(pN) is not very different; then, the part of the expression abs(pN - f(pN)) or abs(pN - c(pN)) is just zero.
The slightly different case is where f(p1) == f(p2):
f(p1) p1 c(p1) f(p1) p1 c(p1)
+---O>>>>+ +<<<O----+
. .
. .
+-----O<<+ +>>>>>O--+
f(p2) p2 c(p2) f(p2) p2 c(p2)
--------------------------------> p axis
In this case, the minimum route can either be p1 -> f(p1) -> f(p2) -> p2 or p1 -> c(p1) -> c(p2) -> p2 (which are Cases 3 and 4, respectively).
As mentioned by 4castle, the problem is trivial if only integers are considered for input. You would never have to "move back" after having "moved forward" in that case since you would always reach your destinaton in a single move.
But since, at most one floating point number needs to be considered for each of departure/destination, we need to consider 3 cases, (warning: lengthy explanation). Below is a python2 implementation with explanations.
x co-ordinates of both departure and destination are the same and are not floating point numbers. In this case, the shortest distance is simply the absolute difference between y co-ordinates. The same logic applies vice-versa.
import math
class Location():
def __init__(self, cord):
self.x = cord[0]
self.y = cord[1]
def perfectCity(departure, destination):
l1 = Location(departure)
l2 = Location(destination)
if l1.x == l2.x and float(l1.x).is_integer() and float(l2.x).is_integer():
return abs(l1.y-l2.y)
if l1.y == l2.y and float(l1.y).is_integer() and float(l2.y).is_integer():
return abs(l1.x-l2.x)
When one of the co-ordinates in the departure is a floating point, then:
If x co-ordinate is floating point, we can move backwards(round down) or move front(round up).
If y co-ordinate is floating point, we can either move down (round down) or move up (round up).
The above logic should work even when there is no floating point co-ordinate since we move in either direction by zero units.
Once we calculate these, we just choose the minimum among those like below,
return min(calc_round_up_dist(l1, l2), cal_round_down_dist(l1, l2))
Lets take an example of (0.4, 1) and (0.9, 3) for below calculations.
While calculating the round_up we need to calculate 3 distances:
round_up_distance: difference between the rounded up value of the floating point co-ordinate and the original floating point co-ordinate. We return zero if there is no floating point co-ordinate. 1 - 0.4 = 0.6 in the above example
non_floating_point difference: difference between the non floating point co-ordinate of departure and the corresponding co-ordinate of destination ( note that this might be floating point or not a floating point abs(3-1) = 2 in the above example
Difference between departure's floating point counterpart in the destination co-ordinate 0.9 in the above case and the new value of departure's floating point after rounding up, 0.4 + 0.6(this is the round_up distance) = 1.0, i.e. abs(0.9 - 1.0) = 0.1
Adding all the 3 above we get 0.6 + 2 + .1 = 2.7 which is the shortest distance.
Corresponding calculation needs to be done for rounding down. And we pick the minimum among both. The code for round_up and round_down is as below,
import math
class Location():
def __init__(self, cord):
self.x = cord[0]
self.y = cord[1]
def floating_point_round_up(self):
if not float(self.x).is_integer():
return math.ceil(self.x) - self.x
if not float(self.y).is_integer():
return math.ceil(self.y) - self.y
return 0
def floating_point_round_down(self):
if not float(self.x).is_integer():
return self.x - math.floor(self.x)
if not float(self.y).is_integer():
return self.y - math.floor(self.y)
return 0
def non_floating_point_diff(self, obj):
if not float(self.x).is_integer():
return abs(self.y - obj.y)
if not float(self.y).is_integer():
return abs(self.x - obj.x)
return abs(self.y - obj.y)
def floating_point_counterpart(self, obj):
if not float(self.x).is_integer():
return obj.x
if not float(self.y).is_integer():
return obj.y
return obj.x
def floating_point(self):
if not float(self.x).is_integer():
return self.x
if not float(self.y).is_integer():
return self.y
return self.x
Round up and down functions are as below,
def calc_round_up_dist(l1, l2):
dist = l1.floating_point_round_up()
diff = l1.non_floating_point_diff(l2)
floating_point_counterpart = l1.floating_point_counterpart(l2)
new_val = dist + l1.floating_point()
return dist + diff + abs(new_val - floating_point_counterpart)
def cal_round_down_dist(l1, l2):
dist = l1.floating_point_round_down()
diff = l1.non_floating_point_diff(l2)
floating_point_counterpart = l1.floating_point_counterpart(l2)
new_val = l1.floating_point() - dist
return dist + diff + abs(floating_point_counterpart - new_val)
Finally the main function that calls the above methods,
def perfectCity(departure, destination):
l1 = Location(departure)
l2 = Location(destination)
if l1.x == l2.x and float(l1.x).is_integer() and float(l2.x).is_integer():
return abs(l1.y-l2.y)
if l1.y == l2.y and float(l1.y).is_integer() and float(l2.y).is_integer():
return abs(l1.x-l2.x)
return min(calc_round_up_dist(l1, l2), cal_round_down_dist(l1, l2))
This is a practice question on CodeSignal:
https://app.codesignal.com/company-challenges/uber/gsjPcfsuNavxhsQQ7
def solution(departure, destination):
# go to the closest integer
# I can only travel the path of an integer
# if the number is a float I need to travel to an integer first
# then travel to the destination
x1, y1 = departure
x2, y2 = destination
res = 0
# check if the coordinations are integers
a = list(map(isInteger, departure))
b = list(map(isInteger, destination))
# if all are integers or if the corresponding elements are different
if a[0] ^ b[0] or (a[0] and a[1] and b[0] and b[1]):
return abs(x1-x2) + abs(y1-y2)
if a[0]:
res += abs(x2-x1)
# cloest distance from y1 to y2
res += getClosest(y1, y2)
else:
res += abs(y2-y1)
# closes distance from x1 to x2
res += getClosest(x1, x2)
return res
def getClosest(y1, y2):
cand1 = math.floor(y1)
cand2 = math.ceil(y1)
# find the integer closer to y1 to move to
res1 = abs(y1-cand1) + abs(cand1-y2)
res2 = abs(y1-cand2) + abs(cand2-y2)
return min(res1, res2)
def isInteger(n):
return n == round(n)

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

Categories