001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2001, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.resources;
018:
019: // J2SE dependencies
020: import java.text.ChoiceFormat;
021:
022: // Geotools dependencies
023: import org.geotools.resources.i18n.Errors;
024: import org.geotools.resources.i18n.ErrorKeys;
025:
026: /**
027: * Simple mathematical functions. Some of these functions will be removed if JavaSoft provide a
028: * standard implementation or fix some issues in Bug Parade:
029: * <p>
030: * <ul>
031: * <li><a href="http://developer.java.sun.com/developer/bugParade/bugs/4074599.html">Implement log10 (base 10 logarithm)</a></li>
032: * <li><a href="http://developer.java.sun.com/developer/bugParade/bugs/4358794.html">implement pow10 (power of 10) with optimization for integer powers</a>/li>
033: * <li><a href="http://developer.java.sun.com/developer/bugParade/bugs/4461243.html">Math.acos is very slow</a></li>
034: * </ul>
035: *
036: * @since 2.0
037: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/resources/XMath.java $
038: * @version $Id: XMath.java 26695 2007-08-23 18:58:56Z desruisseaux $
039: * @author Martin Desruisseaux
040: */
041: public final class XMath {
042: /**
043: * Natural logarithm of 10.
044: * Approximately equal to 2.302585.
045: */
046: public static final double LN10 = 2.3025850929940456840179914546844;
047:
048: /**
049: * Table of some integer powers of 10. Used
050: * for fast computation of {@link #pow10(int)}.
051: */
052: private static final double[] POW10 = { 1E+00, 1E+01, 1E+02, 1E+03,
053: 1E+04, 1E+05, 1E+06, 1E+07, 1E+08, 1E+09, 1E+10, 1E+11,
054: 1E+12, 1E+13, 1E+14, 1E+15, 1E+16, 1E+17, 1E+18, 1E+19,
055: 1E+20, 1E+21, 1E+22 };
056:
057: /**
058: * Do not allow instantiation of this class.
059: */
060: private XMath() {
061: }
062:
063: /**
064: * Combute the cubic root of the specified value. This is method will be removed if
065: * <A HREF="http://developer.java.sun.com/developer/bugParade/bugs/4633024.html">RFE
066: * 4633024</A> is implemented.
067: *
068: * @todo Remove this method when we will be allowed to use J2SE 1.5.
069: */
070: public static double cbrt(final double x) {
071: return Math.pow(x, 1.0 / 3);
072: }
073:
074: /**
075: * Compute the hypotenuse (<code>sqrt(x²+y²)</code>).
076: *
077: * @todo Remove this method when we will be allowed to use J2SE 1.5.
078: */
079: public static double hypot(final double x, final double y) {
080: return Math.sqrt(x * x + y * y);
081: }
082:
083: /**
084: * Compute the logarithm in base 10. See
085: * http://developer.java.sun.com/developer/bugParade/bugs/4074599.html.
086: *
087: * @todo Remove this method when we will be allowed to use J2SE 1.5.
088: */
089: public static double log10(final double x) {
090: return Math.log(x) / LN10;
091: }
092:
093: /**
094: * Compute 10 power <var>x</var>.
095: */
096: public static double pow10(final double x) {
097: final int ix = (int) x;
098: if (ix == x) {
099: return pow10(ix);
100: } else {
101: return Math.pow(10, x);
102: }
103: }
104:
105: /**
106: * Compute <var>x</var> to the power of 10. This computation is very fast
107: * for small power of 10 but has some rounding error issues (see
108: * http://developer.java.sun.com/developer/bugParade/bugs/4358794.html).
109: */
110: public static strictfp double pow10(final int x) {
111: if (x >= 0) {
112: if (x < POW10.length) {
113: return POW10[x];
114: }
115: } else if (x != Integer.MIN_VALUE) {
116: final int nx = -x;
117: if (nx < POW10.length) {
118: return 1 / POW10[nx];
119: }
120: }
121: try {
122: /*
123: * Note: Method 'Math.pow(10,x)' has rounding errors: it doesn't
124: * always return the closest IEEE floating point
125: * representation. Method 'Double.parseDouble("1E"+x)' gives
126: * as good or better numbers for ALL integer powers, but is
127: * much slower. The difference is usually negligible, but
128: * powers of 10 are a special case since they are often
129: * used for scaling axes or formatting human-readable output.
130: * We hope that the current workaround is only temporary.
131: * (see http://developer.java.sun.com/developer/bugParade/bugs/4358794.html).
132: */
133: return Double.parseDouble("1E" + x);
134: } catch (NumberFormatException exception) {
135: return StrictMath.pow(10, x);
136: }
137: }
138:
139: /**
140: * Returns the sign of <var>x</var>. This method returns
141: * -1 if <var>x</var> is negative,
142: * 0 if <var>x</var> is null or {@code NaN} and
143: * +1 if <var>x</var> is positive.
144: *
145: * @todo Consider removing this method when we will be allowed to use J2SE 1.5.
146: * Note: the J2SE 1.5 method is actually not an exact replacement (it
147: * returns a double type instead, which may lead to tricky issues relative
148: * to NaN and negative zero).
149: */
150: public static int sgn(final double x) {
151: if (x > 0)
152: return +1;
153: if (x < 0)
154: return -1;
155: else
156: return 0;
157: }
158:
159: /**
160: * Returns the sign of <var>x</var>. This method returns
161: * -1 if <var>x</var> is negative,
162: * 0 if <var>x</var> is null or {@code NaN} and
163: * +1 if <var>x</var> is positive.
164: *
165: * @todo Remove this method when we will be allowed to use J2SE 1.5.
166: */
167: public static int sgn(final float x) {
168: if (x > 0)
169: return +1;
170: if (x < 0)
171: return -1;
172: else
173: return 0;
174: }
175:
176: /**
177: * Returns the sign of <var>x</var>. This method returns
178: * -1 if <var>x</var> is negative,
179: * 0 if <var>x</var> is null and
180: * +1 if <var>x</var> is positive.
181: *
182: * @todo Remove this method when we will be allowed to use J2SE 1.5.
183: */
184: public static int sgn(long x) {
185: if (x > 0)
186: return +1;
187: if (x < 0)
188: return -1;
189: else
190: return 0;
191: }
192:
193: /**
194: * Returns the sign of <var>x</var>. This method returns
195: * -1 if <var>x</var> is negative,
196: * 0 if <var>x</var> is null and
197: * +1 if <var>x</var> is positive.
198: *
199: * @todo Remove this method when we will be allowed to use J2SE 1.5.
200: */
201: public static int sgn(int x) {
202: if (x > 0)
203: return +1;
204: if (x < 0)
205: return -1;
206: else
207: return 0;
208: }
209:
210: /**
211: * Returns the sign of <var>x</var>. This method returns
212: * -1 if <var>x</var> is negative,
213: * 0 if <var>x</var> is null and
214: * +1 if <var>x</var> is positive.
215: *
216: * @todo Remove this method when we will be allowed to use J2SE 1.5.
217: */
218: public static short sgn(short x) {
219: if (x > 0)
220: return (short) +1;
221: if (x < 0)
222: return (short) -1;
223: else
224: return (short) 0;
225: }
226:
227: /**
228: * Returns the sign of <var>x</var>. This method returns
229: * -1 if <var>x</var> is negative,
230: * 0 if <var>x</var> is null and
231: * +1 if <var>x</var> is positive.
232: *
233: * @todo Remove this method when we will be allowed to use J2SE 1.5.
234: */
235: public static byte sgn(byte x) {
236: if (x > 0)
237: return (byte) +1;
238: if (x < 0)
239: return (byte) -1;
240: else
241: return (byte) 0;
242: }
243:
244: /**
245: * Round the specified value, providing that the difference between the original value and
246: * the rounded value is not greater than the specified amount of floating point units. This
247: * method can be used for hiding floating point error likes 2.9999999996.
248: *
249: * @param value The value to round.
250: * @param flu The amount of floating point units.
251: * @return The rounded value, of {@code value} if it was not close enough to an integer.
252: */
253: public static double round(final double value, int flu) {
254: final double target = Math.rint(value);
255: if (value != target) {
256: final boolean pos = (value < target);
257: double candidate = value;
258: while (--flu >= 0) {
259: candidate = pos ? next(candidate) : previous(candidate);
260: if (candidate == target) {
261: return target;
262: }
263: }
264: }
265: return value;
266: }
267:
268: /**
269: * Try to remove at least {@code n} fraction digits in the string representation of
270: * the specified value. This method try small changes to {@code value}, by adding or
271: * substracting a maximum of 4 ulps. If there is no small change that remove at least
272: * {@code n} fraction digits, then the value is returned unchanged. This method is
273: * used for hiding rounding errors, like in conversions from radians to degrees.
274: *
275: * <P>Example: {@code XMath.fixRoundingError(-61.500000000000014, 12)} returns
276: * {@code -61.5}.
277: *
278: * @param value The value to fix.
279: * @param n The minimum amount of fraction digits.
280: * @return The fixed value, or the unchanged {@code value} if there is no small change
281: * that remove at least {@code n} fraction digits.
282: */
283: public static double fixRoundingError(final double value, int n) {
284: double lower = value;
285: double upper = value;
286: n = countFractionDigits(value) - n;
287: if (n > 0) {
288: for (int i = 0; i < 4; i++) {
289: if (countFractionDigits(lower = previous(lower)) <= n)
290: return lower;
291: if (countFractionDigits(upper = next(upper)) <= n)
292: return upper;
293: }
294: }
295: return value;
296: }
297:
298: /**
299: * Count the fraction digits in the string representation of
300: * the specified value. This method is equivalent to a call to
301: * <code>{@linkplain Double#toString(double) Double#toString}(value)</code>
302: * and counting the number of digits after the decimal separator.
303: */
304: public static int countFractionDigits(final double value) {
305: final String asText = Double.toString(value);
306: final int exp = asText.indexOf('E');
307: int upper, power;
308: if (exp >= 0) {
309: upper = exp;
310: power = Integer.parseInt(asText.substring(exp + 1));
311: } else {
312: upper = asText.length();
313: power = 0;
314: }
315: while ((asText.charAt(--upper)) == '0')
316: ;
317: return Math.max(upper - asText.indexOf('.') - power, 0);
318: }
319:
320: /**
321: * Finds the least float greater than d (if positive == true),
322: * or the greatest float less than d (if positive == false).
323: * If NaN, returns same value. This code is an adaptation of
324: * {@link java.text.ChoiceFormat#nextDouble}.
325: *
326: * @todo Remove this method when we will be allowed to use J2SE 1.5.
327: */
328: private static float next(final float f, final boolean positive) {
329: final int SIGN = 0x80000000;
330: final int POSITIVEINFINITY = 0x7F800000;
331:
332: // Filter out NaN's
333: if (Float.isNaN(f)) {
334: return f;
335: }
336:
337: // Zero's are also a special case
338: if (f == 0f) {
339: final float smallestPositiveFloat = Float.intBitsToFloat(1);
340: return (positive) ? smallestPositiveFloat
341: : -smallestPositiveFloat;
342: }
343:
344: // If entering here, d is a nonzero value.
345: // Hold all bits in a int for later use.
346: final int bits = Float.floatToIntBits(f);
347:
348: // Strip off the sign bit.
349: int magnitude = bits & ~SIGN;
350:
351: // If next float away from zero, increase magnitude.
352: // Else decrease magnitude
353: if ((bits > 0) == positive) {
354: if (magnitude != POSITIVEINFINITY) {
355: magnitude++;
356: }
357: } else {
358: magnitude--;
359: }
360:
361: // Restore sign bit and return.
362: final int signbit = bits & SIGN;
363: return Float.intBitsToFloat(magnitude | signbit);
364: }
365:
366: /**
367: * Finds the least float greater than <var>f</var>.
368: * If {@code NaN}, returns same value.
369: *
370: * @todo Remove this method when we will be allowed to use J2SE 1.5.
371: */
372: public static float next(final float f) {
373: return next(f, true);
374: }
375:
376: /**
377: * Finds the greatest float less than <var>f</var>.
378: * If {@code NaN}, returns same value.
379: *
380: * @todo Remove this method when we will be allowed to use J2SE 1.5.
381: */
382: public static float previous(final float f) {
383: return next(f, false);
384: }
385:
386: /**
387: * Finds the least double greater than <var>f</var>.
388: * If {@code NaN}, returns same value.
389: *
390: * @see java.text.ChoiceFormat#nextDouble
391: *
392: * @todo Remove this method when we will be allowed to use J2SE 1.5.
393: */
394: public static double next(final double f) {
395: return ChoiceFormat.nextDouble(f);
396: }
397:
398: /**
399: * Finds the greatest double less than <var>f</var>.
400: * If {@code NaN}, returns same value.
401: *
402: * @see java.text.ChoiceFormat#previousDouble
403: *
404: * @todo Remove this method when we will be allowed to use J2SE 1.5.
405: */
406: public static double previous(final double f) {
407: return ChoiceFormat.previousDouble(f);
408: }
409:
410: /**
411: * Returns the next or previous representable number. If {@code amount} is equals to
412: * {@code 0}, then this method returns the {@code value} unchanged. Otherwise,
413: * The operation performed depends on the specified {@code type}:
414: * <ul>
415: * <li><p>If the {@code type} is {@link Double}, then this method is
416: * equivalent to invoking {@link #previous(double)} if {@code amount} is equals to
417: * {@code -1}, or invoking {@link #next(double)} if {@code amount} is equals to
418: * {@code +1}. If {@code amount} is smaller than {@code -1} or greater
419: * than {@code +1}, then this method invokes {@link #previous(double)} or
420: * {@link #next(double)} in a loop for {@code abs(amount)} times.</p></li>
421: *
422: * <li><p>If the {@code type} is {@link Float}, then this method is
423: * equivalent to invoking {@link #previous(float)} if {@code amount} is equals to
424: * {@code -1}, or invoking {@link #next(float)} if {@code amount} is equals to
425: * {@code +1}. If {@code amount} is smaller than {@code -1} or greater
426: * than {@code +1}, then this method invokes {@link #previous(float)} or
427: * {@link #next(float)} in a loop for {@code abs(amount)} times.</p></li>
428: *
429: * <li><p>If the {@code type} is an {@linkplain #isInteger integer}, then invoking
430: * this method is equivalent to computing {@code value + amount}.</p></li>
431: * </ul>
432: *
433: * @param type The type. Should be the class of {@link Double}, {@link Float},
434: * {@link Long}, {@link Integer}, {@link Short} or {@link Byte}.
435: * @param value The number to rool.
436: * @param amount -1 to return the previous representable number,
437: * +1 to return the next representable number, or
438: * 0 to return the number with no change.
439: * @return One of previous or next representable number as a {@code double}.
440: * @throws IllegalArgumentException if {@code type} is not one of supported types.
441: */
442: public static double rool(final Class type, double value, int amount)
443: throws IllegalArgumentException {
444: if (Double.class.equals(type)) {
445: if (amount < 0) {
446: do {
447: value = previous(value);
448: } while (++amount != 0);
449: } else if (amount != 0) {
450: do {
451: value = next(value);
452: } while (--amount != 0);
453: }
454: return value;
455: }
456: if (Float.class.equals(type)) {
457: float vf = (float) value;
458: if (amount < 0) {
459: do {
460: vf = previous(vf);
461: } while (++amount != 0);
462: } else if (amount != 0) {
463: do {
464: vf = next(vf);
465: } while (--amount != 0);
466: }
467: return vf;
468: }
469: if (isInteger(type)) {
470: return value + amount;
471: }
472: throw new IllegalArgumentException(Errors.format(
473: ErrorKeys.UNSUPPORTED_DATA_TYPE_$1, Utilities
474: .getShortName(type)));
475: }
476:
477: /**
478: * Returns a {@link Float#NaN NaN} number for the specified index. Valid NaN numbers have
479: * bit fields ranging from {@code 0x7f800001} through {@code 0x7fffffff} or {@code 0xff800001}
480: * through {@code 0xffffffff}. The standard {@link Float#NaN} has bit fields {@code 0x7fc00000}.
481: *
482: * @param index The index, from -2097152 to 2097151 inclusive.
483: * @return One of the legal {@link Float#NaN NaN} values as a float.
484: * @throws IndexOutOfBoundsException if the specified index is out of bounds.
485: */
486: public static float toNaN(int index)
487: throws IndexOutOfBoundsException {
488: index += 0x200000;
489: if (index >= 0 && index <= 0x3FFFFF) {
490: final float value = Float
491: .intBitsToFloat(0x7FC00000 + index);
492: assert Float.isNaN(value) : value;
493: return value;
494: } else {
495: throw new IndexOutOfBoundsException(Integer
496: .toHexString(index));
497: }
498: }
499:
500: /**
501: * Returns {@code true} if the specified {@code type} is one of real
502: * number types. Real number types includes {@link Float} and {@link Double}.
503: *
504: * @param type The type to test (may be {@code null}).
505: * @return {@code true} if {@code type} is the class {@link Float} or {@link Double}.
506: */
507: public static boolean isReal(final Class type) {
508: return type != null && Double.class.equals(type)
509: || Float.class.equals(type);
510: }
511:
512: /**
513: * Returns {@code true} if the specified {@code type} is one of integer types.
514: * Integer types includes {@link Long}, {@link Integer}, {@link Short} and {@link Byte}.
515: *
516: * @param type The type to test (may be {@code null}).
517: * @return {@code true} if {@code type} is the class {@link Long}, {@link Integer},
518: * {@link Short} or {@link Byte}.
519: */
520: public static boolean isInteger(final Class type) {
521: return type != null && Long.class.equals(type)
522: || Integer.class.equals(type)
523: || Short.class.equals(type) || Byte.class.equals(type);
524: }
525:
526: /**
527: * Returns the number of bits used by number of the specified type.
528: *
529: * @param type The type (may be {@code null}).
530: * @return The number of bits, or 0 if unknow.
531: *
532: * @todo Use the predefined constants when we will be allowed to use J2SE 1.5.
533: */
534: public static int getBitCount(final Class type) {
535: if (Double.class.equals(type))
536: return 64;
537: if (Float.class.equals(type))
538: return 32;
539: if (Long.class.equals(type))
540: return 64;
541: if (Integer.class.equals(type))
542: return 32;
543: if (Short.class.equals(type))
544: return 16;
545: if (Byte.class.equals(type))
546: return 8;
547: if (Character.class.equals(type))
548: return 16;
549: if (Boolean.class.equals(type))
550: return 1;
551: return 0;
552: }
553:
554: /**
555: * Change a primitive class to its wrapper (e.g. {@code double} to {@link Double}).
556: * If the specified class is not a primitive type, then it is returned unchanged.
557: *
558: * @param type The primitive type (may be {@code null}).
559: * @return The type as a wrapper.
560: */
561: public static Class primitiveToWrapper(final Class type) {
562: if (Character.TYPE.equals(type))
563: return Character.class;
564: if (Boolean.TYPE.equals(type))
565: return Boolean.class;
566: if (Byte.TYPE.equals(type))
567: return Byte.class;
568: if (Short.TYPE.equals(type))
569: return Short.class;
570: if (Integer.TYPE.equals(type))
571: return Integer.class;
572: if (Long.TYPE.equals(type))
573: return Long.class;
574: if (Float.TYPE.equals(type))
575: return Float.class;
576: if (Double.TYPE.equals(type))
577: return Double.class;
578: return type;
579: }
580:
581: /**
582: * Converts the specified string into a value object. The value object will be an instance
583: * of {@link Boolean}, {@link Integer}, {@link Double}, <cite>etc.</cite> according the
584: * specified type.
585: *
586: * @param type The requested type.
587: * @param value the value to parse.
588: * @return The value object, or {@code null} if {@code value} was null.
589: * @throws IllegalArgumentException if {@code type} is not a recognized type.
590: * @throws NumberFormatException if the string value is not parseable as a number
591: * of the specified type.
592: *
593: * @since 2.4
594: */
595: public static/*T*/Object valueOf(final Class/*<T>*/type,
596: final String value) throws IllegalArgumentException,
597: NumberFormatException {
598: if (value == null) {
599: return null;
600: }
601: if (Double.class.equals(type))
602: return Double.valueOf(value);
603: if (Float.class.equals(type))
604: return Float.valueOf(value);
605: if (Long.class.equals(type))
606: return Long.valueOf(value);
607: if (Integer.class.equals(type))
608: return Integer.valueOf(value);
609: if (Short.class.equals(type))
610: return Short.valueOf(value);
611: if (Byte.class.equals(type))
612: return Byte.valueOf(value);
613: if (Boolean.class.equals(type))
614: return Boolean.valueOf(value);
615: throw new IllegalArgumentException(Errors.format(
616: ErrorKeys.UNKNOW_TYPE_$1, type));
617: }
618: }
|