Is there any benefit to using Math.Floor over explicit integer casting?

Shadow picture Shadow · Dec 30, 2014 · Viewed 8k times · Source

Question is pretty straightforward, is there any benefit or difference? I've noticed that in C# the function returns a double without any decimal places, while in java it keeps the decimal places, but other than that the result is the same.

Here is the code I used in Java and C#, and the output:

//Java                                        //C#

double a = 5.5;                               double a = 5.5;
System.out.println(Math.floor(a));            Console.WriteLine(Math.Floor(a));
System.out.println((int)a);                   Console.WriteLine((int)a);

//Output                                      //Output

5.0                                           5
5                                             5

Answer

Willem Van Onsem picture Willem Van Onsem · Dec 30, 2014

Yes, for negative numbers, this works in the opposite way.

Example (using Mono's C# interactive shell csharp):

csharp> Math.Floor(-12.0d)
-12
csharp> Math.Floor(-12.5d) 
-13
csharp> (int) -12.5
-12

(same for both Java/C#) and I guess most languages anyway.

Casting a floating-point number to an integer, is performed by throwing away the decimal part maintaining the integer part. The integer part of -12.5 is 12. Thus negative numbers do a Math.Ceil if converted to an int.

Furthermore as @Matthew argues, a float or double can reach numbers like 1.023e23 (Avogadro's constant). Simply because the mantisse can't represent digits after the comma anymore. Numbers that are considered to be an integer anyway, but can't be represented by an int. By performing a Math.floor operation, nothing happens, but the value is still maintained. While conversion to an int could result in overflow:

Example:

csharp> double ac = 1.023e23;
csharp> Math.Floor(ac);
1.023E+23
csharp> (int) ac;
0

Note: this may look far fetched, but there is thus a clear difference in semantics nevertheless. A difference that tends to lead to errors anyway.

In addition, it works differently for infinite numbers and NaN:

System.out.println(Math.floor(Double.POSITIVE_INFINITY)); // Prints Infinity
System.out.println((int)Double.POSITIVE_INFINITY);        // Prints 2147483647
System.out.println(Math.floor(Double.NaN));               // Prints NaN
System.out.println((int)Double.NaN);                      // Prints 0

But I would always use them nevertheless. Casting makes things way more unreadable. Rounding up/down/off is more some kind of (side-effect) of the cast. By using Math.Ceil/Floor/Round it is clear what you mean.

Sometimes a cast to an integer is indeed a bit more efficient than performing a floor/ceil operation first. But a smart compiler can sometimes derive that a variable will always store a positive number and thus optimize it itself. And furthermore for most applications this will result in an insignificant performance penalty.