2

So I've been building some unit conversion tools, and here's a snippet of code which contains the issue I have:

public static void main(String args[]) {
    double value = 100;
    System.out.println("Value in millimeters: " + value);
    value = ConversionTools.toFeet(ConversionTools.MILLIMETER, value);
    System.out.println("Value in feet: " + value);
    System.exit(0);
}
class ConversionTools {
// Start constants
public static final int MILLIMETER      = 0;
public static final int CENTIMETER      = 1;
public static final int DECIMETER       = 2;
public static final int METER           = 3;
public static final int DECAMETER       = 4;
public static final int HECTOMETER      = 5;
public static final int KILOMETER       = 6;
public static final int INCH            = 7;
public static final int FOOT            = 8;
public static final int YARD            = 9;
public static final int MILE            = 10;

private static final double KILOMETERS_TO_HECTOMETERS = 10;
private static final double HECTOMETERS_TO_DECAMETERS = 10;
private static final double DECAMETERS_TO_METERS = 10;
private static final double METERS_TO_DECIMETERS = 10;
private static final double DECIMETERS_TO_CENTIMETERS = 10;
private static final double CENTIMETERS_TO_MILLIMETERS = 10;

private static final double MILES_TO_YARDS = 1760;
private static final double YARDS_TO_FEET = 3;
private static final double FEET_TO_INCHES = 12;

private static final double METERS_TO_FEET = 3.28084;
// End constants

/**
 * Converts a distance to meters
 * @param type the type of distance
 * @param value the value of the distance
 * @return  returns the converted distance, or Double.MIN_VALUE
 * if the type is not recognized
 */
public static double toMeters(int type, double value) {
    if (type == MILLIMETER)         return toMeters(CENTIMETER, (double) value / CENTIMETERS_TO_MILLIMETERS);
    else if (type == CENTIMETER)    return toMeters(DECIMETER, (double) value / DECIMETERS_TO_CENTIMETERS);
    else if (type == DECIMETER)     return toMeters(METER, (double) value / METERS_TO_DECIMETERS);
    else if (type == METER)         return value;
    else if (type == DECAMETER)     return toMeters(METER, (double) value * DECAMETERS_TO_METERS);
    else if (type == HECTOMETER)    return toMeters(DECAMETER, (double) value * HECTOMETERS_TO_DECAMETERS);
    else if (type == KILOMETER)     return toMeters(HECTOMETER, (double) value * KILOMETERS_TO_HECTOMETERS);
    else if (type == INCH)          return toMeters(FOOT, toFeet(INCH, value));
    else if (type == FOOT)          return toMeters(METER, (double) value / METERS_TO_FEET);
    else if (type == YARD)          return toMeters(FOOT, toFeet(YARD, value));
    else if (type == MILE)          return toMeters(FOOT, toFeet(MILE, value));
    else                            return Double.MIN_VALUE;
}
/**
 * Converts a distance to feet
 * @param type the type of distance
 * @param value the value of the distance
 * @return returns the converted distance, or Double.MIN_VALUE
 * if the type is not recognized
 */
public static double toFeet(int type, double value) {
    if (type == INCH)               return toFeet(FOOT, (double) value / FEET_TO_INCHES);
    else if (type == FOOT)          return value;
    else if (type == YARD)          return toFeet(FOOT, (double) value * YARDS_TO_FEET);
    else if (type == MILE)          return toFeet(YARD, (double) value * MILES_TO_YARDS);
    else if (type == MILLIMETER)    return toFeet(METER, toMeters(MILLIMETER, value));
    else if (type == CENTIMETER)    return toFeet(METER, toMeters(CENTIMETER, value));
    else if (type == DECIMETER)     return toFeet(METER, toMeters(DECIMETER, value));
    else if (type == METER)         return toFeet(FOOT, (double) value * METERS_TO_FEET);
    else if (type == DECAMETER)     return toFeet(METER, toMeters(DECAMETER, value));
    else if (type == HECTOMETER)    return toFeet(METER, toMeters(HECTOMETER, value));
    else if (type == KILOMETER)     return toFeet(METER, toMeters(KILOMETER, value));
    else                            return Double.MIN_VALUE;
}

When I run this code, I end up getting this output:

Value in millimeters: 100.0
Value in feet: 0.32808400000000004

Does anyone know why I'm getting this extra 0.4E-16?

Also, for those who have an issue with the constants for the units of distance, I know I can use an enumerator for it but right now for simplicity's sake I'm using integers.

SirtyStan
  • 25
  • 3
  • just a side note, try to use switch/case/default instead of if/else-if/else – A4L Dec 02 '13 at 21:33
  • Floating point is fun. Try comparing `0.1 * 0.1` and `0.1 / 10`. – Zong Dec 02 '13 at 21:37
  • When you want precision use BigDecimal. – Luke Dec 02 '13 at 21:39
  • @A4L I'm actually still a novice at Java and coding in general, and haven't learned switch/case/default yet. - MattBall I did a quick check and couldn't find it, but thank you. – SirtyStan Dec 02 '13 at 21:41
  • See also http://stackoverflow.com/q/588004/139010 and [many, _many_ others](http://stackoverflow.com/search?q=floating+point+broken) – Matt Ball Dec 02 '13 at 21:44

2 Answers2

4

Because Java implements an IEEE standard for floating point numbers. Operations with those numbers are usually not 100% precise.

Here is a very old Java article which should give you more details.

http://www.javaworld.com/jw-10-1996/jw-10-hood.html

peter.petrov
  • 38,363
  • 16
  • 94
  • 159
2

This is down to the precision of your data types. See this link for lots of explanation:

http://blog.stata.com/2012/04/02/the-penultimate-guide-to-precision/

Polynomial
  • 3,656
  • 23
  • 36