Developing programs in Apex that perform mathematical calculations can be tricky. Even when you think that your calculations are error free, your unit tests pass, your functional tests pass, and your end users have accepted the calculations, you may still have errors but just not have tested with the specific data needed to cause the errors. In this article I detail a few issues to be mindful of when developing calculations in Apex.

### Integers

The integer types in Apex are Integer (32-bit representation) and Long (64-bit representation). Integers can represent all of the integers between -2^31 and 2^31-1, inclusive. Longs can represent many more integers: -2^63 through 2^63-1. The data type of a literal integer (e.g., -500, 5550, 2222, 10000, etc.) is Integer, though there is support of Long literals through the use of the ‘L’ suffix (e.g., 2147483648L, 2147483649L, etc.).

### Real Numbers

The two data types that can be used to represent real numbers (i.e., numbers with decimal points) are Double and Decimal (there is no Float). Doubles are 64-bit numbers that range from -2^63 to 2^63-1. Decimals have no bounds and can grow to any size, within the constraints of memory / heap. Decimals are the Apex equivalent of Java’s BigDecimal.

### Integer Division

For the most part, programming with Integers is straightforward, although care must be taken with division. Dividing one integer by another integer will yield an integer result and the remainder will be lost. The remainder can be calculated with the Math.mod function in the Math class, though. For example:

1 2 3 4 5 |
Integer x = 5; Integer y = 2; System.assertEquals(2, x / y); System.assertEquals(1, Math.mod(x, y)); |

If you want to store the value of an integer division in a Double or Decimal you might be tempted to do something like the following:

1 2 3 |
Integer x = 5; Integer y = 2; Double d = x / y; |

That will result in d getting assigned the value of 2.0. The Integer division operation is done with 5 and 2 which evaluates to 2. That Integer 2 is then converted to a Double value of 2.0. This type of implicit conversion is sometimes referred to as type coercion.

The solution is to explicitly convert one of the values in the division expression to a Double *before* the division operation is performed.

1 2 3 |
Integer x = 5; Integer y = 2; Double d = ((Double) x) / y; |

In this case the Integer x is casted to a Double and the Integer y is promoted to a Double for the duration of the calculation, so that a Double division can be performed. The result is 2.5.

### Overflow

If you are programming near the extreme values of integers you can cause overflow. Overflow in Apex is handled by wrapping around to the other extreme. Going past the greatest Integer will yield a large negative result and going past the smallest Integer will yield a large positive result. Here’s an example:

1 2 3 4 5 |
System.debug(2147483647 + 1); // result is -2147483648 System.debug(-2147483647 - 2); // result is 2147483647 |

### Floating Point

The Double data type is a floating point representation of real numbers (see this article for an overview or this article for a deep dive). A key point to understand with Doubles is that not all real numbers can be represented by Doubles. This can lead to non-intuitive issues when dealing with arithmetic and comparisons.

For example, you may have a simple algorithm that adds some numbers together and expects their sum to be a certain exact value, but it ends up not yielding expected values.

1 2 3 4 5 6 7 |
Double sum = 0.0; Double pointOne = 0.1; for (Integer i = 0; i < 10; i++) { sum = sum + pointOne; } // fails System.assertEquals(1.0, sum, 'huh? expected to be equal'); |

The above code is very straightforward. It simply add 0.1 to a sum 10 times which should equal 1.0. However, it ends up actually being represented as 0.9999999999999999. This is due to the inexact representations. If your code needs more accuracy, you should use the Decimal class. Decimal should always be used for monetary calculations and Double should almost never be used for them. Running that same code again, but with Decimal instead of Double, results in the expected behavior of the sum variable equaling 1.0.

The above loop can be rewritten to not use the pointOne variable, but instead use a constant of 0.1.

1 2 3 4 5 6 |
Double sum = 0.0; for (Integer i = 0; i < 10; i++) { sum = sum + 0.1; } // passes System.assertEquals(1.0, sum, ‘hey! these really are equal’); |

In this situation the assert statement passes and 1.0 equals sum. The reason for this is that the type of a constant with decimal places in Apex is Decimal. In the `sum + 0.1`

expression the sum gets promoted to a Decimal for a Decimal addition operation and the result is a Decimal which is then assigned to the Double sum. The lack of precision that would have been encountered with a Double addition operation is avoided because the Decimal addition operation is done instead. To reproduce the assertion failure with a constant, a Double literal must be used, which is achieved by appending a ‘d’ after the number.

1 2 3 4 5 6 |
Double sum = 0.0; for (Integer i = 0; i < 10; i++) { sum = sum + 0.1d; // note the 'd' } // fails System.assertEquals(1.0, sum, ‘ahh...unequal again’); |

### Rounding Errors Compounded

Using Decimal does take care of many of the issues that can arise from using Doubles; however, Decimals are still not 100% full-proof for every use case. Individual rounding errors can compound to result in undesired behavior.

Consider an investment application that allows users to invest money into a generic retirement account that is actually made up of different investment accounts.

A user may have a retirement account that is made up of three different investment accounts (X, Y, and Z) and have specified that each investment account’s share of any deposit should be determined by its current balance relative to the overall balance (i.e., its current percentage of the total). This seems pretty straightforward. The code might look something like the following, where the Balance__c, investmentsTotal, and depositAmount fields are Decimal’s out to two decimal places (i.e., currency).

1 2 3 4 |
for (InvestmentAccount__c investment : investments) { Decimal share = investment.Balance__c / investmentsTotal; investment.Balance__c = investment.Balance__c + (share * depositAmount); } |

If there’s a transfer of $100.00 and X’s balance is $1K, Y’s balance is $3K, and Z’s balance is $6K, then they’d get $10, $30, and $60 respectively. But, what if all three accounts have the exact same balance? X would get 1/3, Y would get 1/3, and Z would get 1/3, so $33.33, $33.33, and $33.33, respectively. $0.01 is lost. The user only gets $99.99 deposited.

The code must be written to handle the rounding error. One way to handle it is to perform the calculations on values with a greater precision, round the answers, and carry the rounding error over to the next investment account.

1 2 3 4 5 6 7 8 |
Decimal roundingError = 0.0; for (InvestmentAccount__c investment : investments) { Decimal share = investment.Balance__c / investmentsTotal; Decimal preRounded = share * depositAmount + roundingError; investment.Balance__c = investment.Balance__c + preRounded; investment.Balance__c.setScale(2); roundingError = (preRounded - investment.Balance__c); } |

### More

This article described a few interesting aspects of performing calculations in Apex (there are many more). More information about Apex and data types in Apex can be found in the Apex Developer’s Guide.