问题
I have the following code...
public class Point {
private double x;
private double y;
static private final double RADTODEG = 180.0d / Math.PI ;
static private final double DEGTORAD = Math.PI / 180.0d;
/**
* Rotates the point by a specific number of radians about a specific origin point.
* @param origin The origin point about which to rotate the point
* @param degrees The number of radians to rotate the point
*/
public void rotateByRadians(Point origin, double radians) {
double cosVal = Math.cos(radians);
double sinVal = Math.sin(radians);
double ox = x - origin.x;
double oy = y - origin.y;
x = origin.x + ox * cosVal - oy * sinVal;
y = origin.y + ox * sinVal + oy * cosVal;
}
/**
* Rotates the point by a specific number of degrees about a specific origin point.
* @param origin The origin point about which to rotate the point
* @param degrees The number of degrees to rotate the point
*/
public void rotateByDegrees(Point origin, double degrees) {
rotateByRadians(origin, degrees * DEGTORAD);
}
/**
* Rotates the point by the specified number of radians about the axis' origin (0,0). To rotate about a specific origin point, see rotateByRadians(Point, double)
* @param radians Measure of radians to rotate the point
*/
public void rotateByRadians(double radians) {
if(isEmpty()) // Since we're rotating about 0,0, if the point is 0,0, don't do anything
return;
double cosVal = Math.cos(radians);
double sinVal = Math.sin(radians);
double newx = x * cosVal - y * sinVal;
double newy = x * sinVal + y * cosVal;
x = newx;
y = newy;
}
/**
* Rotates the point by the specified number of degrees about to the axis' origin (0,0). To rotate about a specific origin point, see rotateByDegrees(Point, double)
* @param degrees Measure of degrees to rotate the point
*/
public void rotateByDegrees(double degrees) {
rotateByRadians(degrees * DEGTORAD);
}
The problem arises when given a point, say 0,200. Calling the rotation (about axis origin 0,0) for 180 degrees it should be (0, -200). The x
coordinate shouldn't have changed. However, it ends up being (-2.4492935982947064E-14, -200). I tried using strictfp
but it doesn't make a difference. This only affects the result if the coordinate being rotated is zero. Nonzero values work fine. Any ideas why this is not accurate?
The code is below:
Point p = new Point(0.0d, 200.0d);
p.rotateByDegrees(180.0d);
System.out.println(p);
Gives output:
shapelib.Point Object {x: -2.4492935982947064E-14 y: -200.0}
回答1:
Float arithmetic is not fully accurate. Error of 10^-14 power is enough in most cases.
If you calculate Math.sin(Math.PI)
you'll get 1.2246467991473532E-16
. Why do you need to get precisely 0 in your case?
回答2:
For better or for worse, that's just the way it is with floating point math. From http://mindprod.com/jgloss/floatingpoint.html:
"Think of float and double as representing physical measurements. No one would complain if their cabinet maker made a desk 6.000000000001 feet long. Analogously, don’t complain about the inevitable tiny errors in floating point arithmetic results e.g. Math. cos( Math.toRadians( 90 ) ) not coming out bang on zero. ( If you want perfection, use int, long, BigInteger or BigDecimal. )"
回答3:
Both the provided answers so far are correct, but they're missing a fundamental point.
The range of possible values a floating point number can take on is not continuous. Rather, it has holes in it. So you can imagine that from 0.1 to 0.2, instead of there being an infinite amount of numbers, there are only a finite number.
That's most of the reason why floating point arithmetic is inaccurate. Computers can't exactly represent every real number you'd like. Instead, they can get to only some small epsilon away from the actual value.
As an example, you can't exactly represent the fraction 2 / 10. If you print out all the decimal places to this number, you'll find it something like 0.20000000000000001.
See here for a more thorough writeup: http://floating-point-gui.de/
来源:https://stackoverflow.com/questions/8100986/java-point-rotation-results-are-not-accurate