简体   繁体   中英

How does conversion from double to int work in Java?

For the following code (Java):

double d = (double) m / n; //m and n are integers, n>0
int i = (int) (d * n);

i == m

Is the last expression always true? If it's not is this always true?:

i = (int) Math.round(d * n);

i == m

int i = (int) (d * n); i == m;

This is false for m=1, n=49.

i = (int) Math.round(d * n); i == m;

My intuition tells me it should be true, but it may be hard to prove rigorously.

The second question you ask concerns how large an ulp is in Java.

If the ulp exceeds 1/(n) , then rounding the multiplication would not recover the original divided int. Typically, larger ulps are associated with larger double values. An ulp associated with a double starts to exceed 1 at around 9E15; if your recovered doubles were around there, then you might finding problems with round() not getting the expected answer. As you are working with int values, though, the largest value of the numerator of your division will be Integer.MAX_VALUE .

The following program tests all the positive integer values of n to see which one causes the largest potential for rounding error when trying to recover the divided int:

  public static void main(String[] args)
  {
    // start with large number
    int m = Integer.MAX_VALUE;
    double d = 0;

    double largestError = 0;
    int bigErrorCause = -1;
    for (int n = 1; n < Integer.MAX_VALUE; n++)
    {
      d = (double) m / n;
      double possibleError = Math.ulp(d) * n;
      if (possibleError > largestError)
      {
        largestError = possibleError;
        bigErrorCause = n;
      }
    }
    System.out.println("int " + bigErrorCause + " causes at most "
        + largestError + " error");
  }

The output is:

int 1073741823 causes at most 4.768371577590358E-7 error

Rounding that using Math.round, then casting to int should recover the original int.

Mathematically it should be true. However you're likely going to get floating point rounding errors that will make it false. You should almost never compare floating point precision numbers using == .

You're much better off comparing them using a threshold like this:

Math.abs( d*n - m ) < 0.000001;

Note that the two statements should be equivalent

i = (int) (d * n);
i = (int) Math.round(d * n);

However for example, if d=3/2 and n=2 , floating point errors might result in i=2.999999999999 which after truncation/rounding is 2.

The first on is definitely not always true. The second one I would say yes it's true, but only because I can't think of a counterexample.

If n is very large, it could possibly be false, I'm not sure really. I know it will be true at least 99% of the time though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM