1

I am having some issues related to the precision with Decimals. Let's take a look at the following scenario:

Decimal total = (5 / 3) * 3;
System.debug(total) // this will print 4.9999999999999999

total = 5 * 3 / 3;
System.debug(total) // this will print 5

The reason seems that in the first operation, the number that arrives at the multiplication is 1.66666666666666666 (Decimal precision)

Is there any know bug from salesforce with this? Because in other languages I don't observe this behavior.

2

3 Answers 3

7

The data type that gives you fine-grained control over precision is Decimal. For example, if you wanted to have 30 decimal places after the divide this:

Decimal d = 5;
d = d.divide(3, 30);
System.debug('>>> ' + d);

outputs this:

>>> 1.666666666666666666666666666667

Take a look at the Decimal documentation to see the various methods available that allow you to control precision.

Decimal is significantly different from Double or Float: they represent numbers using a fixed number of bits and most CPUs have hardware support for calculations using them. Decimal (BigDecimal in Java) on the other hand is implemented in software and handles an open ended number of digits exactly. Unless you are doing massive amounts of calculations the slower performance of Decimal is irrelevant.

If doing calculations involving money, Decimal is a good choice to exactly control rounding and avoid losing precision with large numbers.

On your specific question, when a calculation discards some digits they are not going to re-appear. Financial algorithms often have specific rounding rules defined as part of their specification to address this e.g. COMPOUND INTEREST.

4

When you store certain types of fractions in a Decimal value, their value is approximated, because they can not be exactly represented in the binary format used by computers. While we humans have no problems with 2 / 3 * 3 equaling 2, ordinary programming languages can't express all numbers precisely.

Let's take a look at another example. If we take sqrt(4) * sqrt(4), we get the answer 4. That's because the square root of a number is the number that, when multiplied by itself, yields that number. However, if we now take a look at sqrt(2) * sqrt(2), we end up with a value like 2.0000000000000004, instead of the logical conclusion of 2. This is because irrational numbers can not be expressed in the binary format native to decimal; we end up with only a close approximation.

The general rule for performing math in a programming language is to perform division last whenever possible, even if it means doing some algebra to figure out how to do so. Whenever you perform division, you must assume that some loss of precision will occur. The more times you divide, the greater loss of precision may occur. Generally, this means you'll need to reset the precision of the end result using Decimal.setScale. Additional precision will be lost.

Finally, note that in Apex Code, writing (5 / 3) * 3 literally results in the value of 3, not 4.99999999999999999. This is because those values are interpreted as integers, so you get integer division. To yield a result closer to what you observed, you'd need to introduce a decimal for implicit upcasting:

System.debug((5 / 3) * 3); // 3
System.debug((5.0 / 3) * 3); // 5.00000000000000000000000000000001

This is the exact same behavior as observed in Java.

2

Decimal, Float or Double data type always lose precision when multiplied, added or divided.

Regardless of programming language we use, this behavior is universal. I would like you to go through following post if you want to understand in detail.

What Every Computer Scientist Should Know About Floating-Point Arithmetic

Otherwise, this is easy one, however addition doesn't apply to Salesforce because we have bigger precision data type - Floating point guide

Decimal v/s Double

Double are typically 64 bit number but Decimal is 128 bit. With both types we can have unexpected results based on at which size the result is finished.

Take following example:-

Decimal total = 8.0/3.0; // 2.66666666666666666666666666666667
Decimal total2 = total*3.0; // 8.000000000000000000000000000000010
Double totald = 8.0/3.0; // 2.6666666666666665
Double totald2 = totald*3.0; // 7.999999999999999
System.debug(' ===>'+ (total));
System.debug(' ===>'+ (total2));
System.debug(' ===>'+ (totald));
System.debug(' ===>'+ (totald2));

In above example Decimal had the greater memory than Double so, ended up in more detailed result. This further result into some unexpected result.

2
  • Could you review this case then? imgur.com/a/5Vx77
    – junjs
    Commented Jan 4, 2017 at 17:10
  • @junjs because decimal has capacity to store large digits so at point it loose its precision. Where as Double has less capacity so it fail to get that precision and result in correct result. In your case only. It will not always true.
    – Ashwani
    Commented Jan 4, 2017 at 17:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .