5

I have code like this:

System.out.println("Math.round(1423562400L) => " + Math.round(1423562400L));

And the result is this:

Math.round(1423562400L) => 1423562368

This value is a 64-bit integer but it easily fits inside a 32-bit integer which should fit in a double. What is going on?

swdev
  • 2,941
  • 2
  • 25
  • 37
  • 1
    It works when you cast to `(double)` first. Apparently, it is a `float` otherwise ?! – Thilo May 17 '16 at 03:21
  • 1
    But why does a `long` get automatically cast to a `float`? – swdev May 17 '16 at 03:24
  • @swdev [JLS 15.12.2](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2) tells that both float and double are applicable, and [JLS 15.12.2.5](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2.5) tells float is the **most specific method**. – Nier May 17 '16 at 03:28
  • See also: http://stackoverflow.com/questions/24279680/what-happens-when-we-pass-int-arguments-to-the-overloading-method-having-float-a – Thilo May 17 '16 at 03:28

3 Answers3

7

According to the JLS,

15.12.2.5. Choosing the Most Specific Method

If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.

The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.

It follows that Math.round(float) is more specific than Math.round(double), and since they're both valid overloads, the compiler chooses the former, resulting in lost data.

To correct your output, simply change 1423562400L to 1423562400d.

Community
  • 1
  • 1
shmosel
  • 49,289
  • 6
  • 73
  • 138
  • Can you elaborate why `float` version is more specific than `double` version? So, it tries the float version first and if it compiles then it uses it. But why the float version first. That isn't specified in the JLS. – swdev May 17 '16 at 03:50
  • @swdev Read the second paragraph I quoted. It implies that the float version is more specific because a float can be passed as a double, but not vice-versa (without narrowing conversion). – shmosel May 17 '16 at 03:52
  • Thanks, that explains it. This is weird behavior compared C++. In that language you would get the following `error: call of overloaded 'round(long int)' is ambiguous` passing an integer to a function with both float types defined. – swdev May 17 '16 at 04:10
  • @swdev: Yeah, ambigous overload error might have been the safer choice here. But that boat has sailed. – Thilo May 17 '16 at 04:14
  • @swdev the concept of choosing the more specific overload makes sense to me personally. What was more surprising is that long to float is considered a widening conversion. But as discussed in the answer I linked, you can technically make the same argument about long to double. – shmosel May 17 '16 at 04:14
0

I don't think Math.round() has implementation which supports long argument. Here is the Math.round() signature.

Math.round(double a);
Math.round(float a);

Math.round() accepts double or float, So when you are passing long you are doing something like (long converts to float implicitly)

float x = 1423562400L;
System.out.println(Math.round(x));

To Solve this you can add your long value to double first and it will work like charm.

double x = 1423562400L;
System.out.println(Math.round(x));

To simplify you can do

Math.round((double)1423562400L)

or

Math.round(1423562400d)

Here is the picture that shows the how implicit conversion works and why long gets converted to float

enter image description here

Image source here

A0__oN
  • 8,740
  • 6
  • 40
  • 61
  • Don't post pictures of text here. Post the text. It's a waste of your time, which I don't care about, and our bandwidth, which I do care about. It also prevents copy/pasting into comments. It is basically pointless. – user207421 May 17 '16 at 03:30
  • `long converts to float implicitly`. True, but long also converts to double implicitly (and without loss in this case). But here the "narrower" overload was chosen. – Thilo May 17 '16 at 03:31
  • When conversion takes place. The hierarchy is followed. So first long will be converted to float as we have a round() which supports float. – A0__oN May 17 '16 at 03:36
-1

Notice that the signature of Math.round is

public static int round(float a)

it accepts a 32bit float, now you put a 64bit long to this method, it cut the long to an int, and then turn it to a float.

System.out.println(Math.round((int)1423562400L));
System.out.println(Math.round((float)1423562400L));

the code prints 1423562368 too

if you convert it to double explitly then the result is correct

1423562400

Zheng Kun
  • 56
  • 4