/* * Geotools 2 - OpenSource mapping toolkit * (C) 2003, Geotools Project Managment Committee (PMC) * (C) 2001, Institut de Recherche pour le Développement * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.geotools.resources; // J2SE dependencies import java.text.ChoiceFormat; // Geotools dependencies import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; /** * Simple mathematical functions. Some of these functions will be removed if JavaSoft provide a * standard implementation or fix some issues in Bug Parade: *
*
sqrt(x²+y²)
).
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static double hypot(final double x, final double y) {
return Math.sqrt(x*x + y*y);
}
/**
* Compute the logarithm in base 10. See
* http://developer.java.sun.com/developer/bugParade/bugs/4074599.html.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static double log10(final double x) {
return Math.log(x) / LN10;
}
/**
* Compute 10 power x.
*/
public static double pow10(final double x) {
final int ix = (int) x;
if (ix == x) {
return pow10(ix);
} else {
return Math.pow(10, x);
}
}
/**
* Compute x to the power of 10. This computation is very fast
* for small power of 10 but has some rounding error issues (see
* http://developer.java.sun.com/developer/bugParade/bugs/4358794.html).
*/
public static strictfp double pow10(final int x) {
if (x >= 0) {
if (x < POW10.length) {
return POW10[x];
}
} else if (x != Integer.MIN_VALUE) {
final int nx = -x;
if (nx < POW10.length) {
return 1 / POW10[nx];
}
}
try {
/*
* Note: Method 'Math.pow(10,x)' has rounding errors: it doesn't
* always return the closest IEEE floating point
* representation. Method 'Double.parseDouble("1E"+x)' gives
* as good or better numbers for ALL integer powers, but is
* much slower. The difference is usually negligible, but
* powers of 10 are a special case since they are often
* used for scaling axes or formatting human-readable output.
* We hope that the current workaround is only temporary.
* (see http://developer.java.sun.com/developer/bugParade/bugs/4358794.html).
*/
return Double.parseDouble("1E"+x);
} catch (NumberFormatException exception) {
return StrictMath.pow(10, x);
}
}
/**
* Returns the sign of x. This method returns
* -1 if x is negative,
* 0 if x is null or {@code NaN} and
* +1 if x is positive.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static int sgn(final double x) {
if (x>0) return +1;
if (x<0) return -1;
else return 0;
}
/**
* Returns the sign of x. This method returns
* -1 if x is negative,
* 0 if x is null or {@code NaN} and
* +1 if x is positive.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static int sgn(final float x) {
if (x>0) return +1;
if (x<0) return -1;
else return 0;
}
/**
* Returns the sign of x. This method returns
* -1 if x is negative,
* 0 if x is null and
* +1 if x is positive.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static int sgn(long x) {
if (x>0) return +1;
if (x<0) return -1;
else return 0;
}
/**
* Returns the sign of x. This method returns
* -1 if x is negative,
* 0 if x is null and
* +1 if x is positive.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static int sgn(int x) {
if (x>0) return +1;
if (x<0) return -1;
else return 0;
}
/**
* Returns the sign of x. This method returns
* -1 if x is negative,
* 0 if x is null and
* +1 if x is positive.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static short sgn(short x) {
if (x>0) return (short) +1;
if (x<0) return (short) -1;
else return (short) 0;
}
/**
* Returns the sign of x. This method returns
* -1 if x is negative,
* 0 if x is null and
* +1 if x is positive.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static byte sgn(byte x) {
if (x>0) return (byte) +1;
if (x<0) return (byte) -1;
else return (byte) 0;
}
/**
* Round the specified value, providing that the difference between the original value and
* the rounded value is not greater than the specified amount of floating point units. This
* method can be used for hiding floating point error likes 2.9999999996.
*
* @param value The value to round.
* @param flu The amount of floating point units.
* @return The rounded value, of {@code value} if it was not close enough to an integer.
*/
public static double round(final double value, int flu) {
final double target = Math.rint(value);
if (value != target) {
final boolean pos = (value < target);
double candidate = value;
while (--flu >= 0) {
candidate = pos ? next(candidate) : previous(candidate);
if (candidate == target) {
return target;
}
}
}
return value;
}
/**
* Try to remove at least {@code n} fraction digits in the string representation of
* the specified value. This method try small changes to {@code value}, by adding or
* substracting a maximum of 4 ulps. If there is no small change that remove at least
* {@code n} fraction digits, then the value is returned unchanged. This method is
* used for hiding rounding errors, like in conversions from radians to degrees.
*
* Example: {@code XMath.fixRoundingError(-61.500000000000014, 12)} returns
* {@code -61.5}.
*
* @param value The value to fix.
* @param n The minimum amount of fraction digits.
* @return The fixed value, or the unchanged {@code value} if there is no small change
* that remove at least {@code n} fraction digits.
*/
public static double fixRoundingError(final double value, int n) {
double lower = value;
double upper = value;
n = countFractionDigits(value) - n;
if (n > 0) {
for (int i=0; i<4; i++) {
if (countFractionDigits(lower = previous(lower)) <= n) return lower;
if (countFractionDigits(upper = next (upper)) <= n) return upper;
}
}
return value;
}
/**
* Count the fraction digits in the string representation of
* the specified value. This method is equivalent to a call to
* {@linkplain Double#toString(double) Double#toString}(value)
* and counting the number of digits after the decimal separator.
*/
public static int countFractionDigits(final double value) {
final String asText = Double.toString(value);
final int exp = asText.indexOf('E');
int upper, power;
if (exp >= 0) {
upper = exp;
power = Integer.parseInt(asText.substring(exp+1));
} else {
upper = asText.length();
power = 0;
}
while ((asText.charAt(--upper)) == '0');
return Math.max(upper - asText.indexOf('.') - power, 0);
}
/**
* Finds the least float greater than d (if positive == true),
* or the greatest float less than d (if positive == false).
* If NaN, returns same value. This code is an adaptation of
* {@link java.text.ChoiceFormat#nextDouble}.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
private static float next(final float f, final boolean positive) {
final int SIGN = 0x80000000;
final int POSITIVEINFINITY = 0x7F800000;
// Filter out NaN's
if (Float.isNaN(f)) {
return f;
}
// Zero's are also a special case
if (f == 0f) {
final float smallestPositiveFloat = Float.intBitsToFloat(1);
return (positive) ? smallestPositiveFloat : -smallestPositiveFloat;
}
// If entering here, d is a nonzero value.
// Hold all bits in a int for later use.
final int bits = Float.floatToIntBits(f);
// Strip off the sign bit.
int magnitude = bits & ~SIGN;
// If next float away from zero, increase magnitude.
// Else decrease magnitude
if ((bits > 0) == positive) {
if (magnitude != POSITIVEINFINITY) {
magnitude++;
}
} else {
magnitude--;
}
// Restore sign bit and return.
final int signbit = bits & SIGN;
return Float.intBitsToFloat(magnitude | signbit);
}
/**
* Finds the least float greater than f.
* If {@code NaN}, returns same value.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static float next(final float f) {
return next(f, true);
}
/**
* Finds the greatest float less than f.
* If {@code NaN}, returns same value.
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static float previous(final float f) {
return next(f, false);
}
/**
* Finds the least double greater than f.
* If {@code NaN}, returns same value.
*
* @see java.text.ChoiceFormat#nextDouble
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static double next(final double f) {
return ChoiceFormat.nextDouble(f);
}
/**
* Finds the greatest double less than f.
* If {@code NaN}, returns same value.
*
* @see java.text.ChoiceFormat#previousDouble
*
* @todo Remove this method when we will be allowed to use J2SE 1.5.
*/
public static double previous(final double f) {
return ChoiceFormat.previousDouble(f);
}
/**
* Returns the next or previous representable number. If {@code amount} is equals to
* {@code 0}, then this method returns the {@code value} unchanged. Otherwise,
* The operation performed depends on the specified {@code type}:
*
If the {@code type} is {@link Double}, then this method is * equivalent to invoking {@link #previous(double)} if {@code amount} is equals to * {@code -1}, or invoking {@link #next(double)} if {@code amount} is equals to * {@code +1}. If {@code amount} is smaller than {@code -1} or greater * than {@code +1}, then this method invokes {@link #previous(double)} or * {@link #next(double)} in a loop for {@code abs(amount)} times.
If the {@code type} is {@link Float}, then this method is * equivalent to invoking {@link #previous(float)} if {@code amount} is equals to * {@code -1}, or invoking {@link #next(float)} if {@code amount} is equals to * {@code +1}. If {@code amount} is smaller than {@code -1} or greater * than {@code +1}, then this method invokes {@link #previous(float)} or * {@link #next(float)} in a loop for {@code abs(amount)} times.
If the {@code type} is an {@linkplain #isInteger integer}, then invoking * this method is equivalent to computing {@code value + amount}.