20

Ok I hope I am mising something here as I really don't want this to be a bug.

The following piece of code returns 2. Per my understanding of the ceiling functionality, the result should be 3 because 7/3 is roughly 2.33333 and the ceiling should return 3.

Is this a bug or am I missing something? Thanks.

Integer pageNumber = (Integer) Math.ceil(7/3);

This returns 2 and not 3 as expected.

It works fine on other input. For example:

Integer pageNumber = (Integer) Math.ceil(8/5);

This returns 2 as expected.

  • 2
    Not sure how you can get Math.ceil(8/5) == 2, because the following assertion passes for me: system.assertEquals(1, Math.ceil(8/5)) – Adrian Larson Dec 22 '15 at 5:35
23

This is probably because when you divide an Integer by an Integer you get an Integer back which has the same value as Decimal.round(RoundingMode.DOWN).

system.assertEquals(1, 8/5);
system.assertEquals(8/5, (8.0/5).round(RoundingMode.DOWN);

system.assertEquals(-1, -8/5);
system.assertEquals(-8/5, (-8.0/5).round(RoundingMode.DOWN);

system.assertEquals(2, 7/3);
system.assertEquals(7/3, (7.0/3).round(RoundingMode.DOWN);

If you know you have Integers and you want to get their ceiling, you could do something like:

public static Integer ceiling(Integer x, Integer y)
{
    return Math.ceil(Decimal.valueOf(x).divide(y, /*digits*/ 1));
}
  • Thanks. This works for me as I need to divide values returned by the Standardsetcontroller. getresultsize() and getpagesize(). Will treat them as decimals. – Richard N Dec 22 '15 at 4:23
  • 1
    why doesn't (Integer) Math.ceil(8/5) return 1 though? – user17915 Dec 22 '15 at 5:24
  • 3
    Integer division in most languages (including this one, I assume) is not floor, it's truncation. There's a difference when the result is negative. – BlueRaja - Danny Pflughoeft Dec 22 '15 at 5:38
  • 1
    @BlueRaja-DannyPflughoeft Good point. There is not even a corresponding RoundingMode for that. Interesting. – Adrian Larson Dec 22 '15 at 6:09
  • 1
    It would be DOWN in that table. That naming is horrible; if this is the API you guys have to deal with, I'm sorry... – BlueRaja - Danny Pflughoeft Dec 22 '15 at 6:19
15

Integer Division

In Apex Code, similar to Java, when there are two like numeric data types (e.g. both Integers), then they are calculated in a way that returns the same data type. Integers cannot store fractions, so when you do something like 7/3, the fraction is silently discarded. As far as I know, integer division works in the same for every programming language in the world where the result of integer arithmetic is an integer.In many languages where the result of two integers being divided together results in an integer, the fraction is often dropped entirely, although exceptions do exist (some perform rounding, instead).

Parameter Promotion

You'll notice that there's no function Math.ceil that accepts an Integer. This means if you give it an integer, it will implicitly be cast to a floating point value before being processed by Math.ceil.

Arithmetic Promotion

When two numbers are operated on using the standard operators (+, -, /, *), if one of those parameters are a floating point value, the other one will also automatically become a floating point value. Similarly, if a integer and a long were involved in an operation, both numbers become long, and the return type becomes long. So, the goal is to create a situation where a floating point is returned. For example, this results in the correct result:

Integer x = Math.ceil(7.0/3).intValue();

Here, the 7.0 indicates a floating point operation. You'd also get the same effect if you did this:

Decimal x = 7;
Integer y = 3;
Integer z = Math.ceil(x/y).intValue();

This behavior is well-defined, and mimics the behavior in Java. You can read more about automatic widening conversion in the Java documentation, as well as rules about integer division. You'll find that, while not explicitly mentioned in the Apex Code Developer's Guide (as far as I can tell), it obeys the same rules.

  • Thank you for the brilliant explanation. I should and will pay attention to my numeric data types especially when doing such operations with integers. – Richard N Dec 22 '15 at 4:26
  • 2
    tats cool... so u r a Maths professor as well Mr Fox.. ;-) – Vamsi Krishna Dec 22 '15 at 5:35
  • ^^He indeed is... – Richard N Dec 22 '15 at 6:19
  • 1
    "As far as I know, this is true for every programming language in the world where the result of integer arithmetic is an integer." Not true. The following two statements in PASCAL: WRITELN(9/2); and WRITELN(9 DIV 2); will output 4.5 and 4 respectively. The "/" operator is an implicit floating point divide, and the DIV is an integer divide... – HeartWare Dec 22 '15 at 9:29
  • @HeartWare "...where the result of integer arithmetic is an integer." PASCAL's integer division happens to use a different token (DIV vs /), but doesn't invalidate this statement. The point was, if you ask for integer division, you lose the fractional, while if you ask for floating point, you get the fractional result. It's just that some languages, like PASCAL, are explicit (using two different tokens), while Apex Code uses one overloaded token to mean the same thing, just like C, C++, Java, C#, etc. – sfdcfox Dec 22 '15 at 14:07
4

My understanding is that when you divide an integer by an integer, it will always result in an integer. So when you divide 7/3, it's actually returning 2, which is why the Math.ceil() function is returning 2.

Try doing Math.ceil(7.0/3.0)

Or you can use code such as:

Double seven = 7.0;
Double three = 3.0;
Integer pageNumber = (Integer)Math.ceil(seven/three);

BTW, the Math.ceil(8/5) should be returning 1, not 2.

Does anyone know where in the documentation it talks about this? I couldn't find anything.

  • Beat me by a few seconds writing it up. This is exactly right. You'll need to convert them to Decimal before dividing them. – drakored Dec 22 '15 at 3:49
  • Got it thanks. Same here...Found nothing on the documentation. It will be great to find something. – Richard N Dec 22 '15 at 4:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.