Never, ever use XI’s built-in arithmetic functions
Latest news
Most of the issues described in this blog have been rectified as of SP18. See my blog New Arithmetic and Statistical Functions in Message Mappings in SP18 for details.
Introduction
The built-in arithmetic funtions in XI’s Message Mappings include common functions like add, subtract, multiply, and divide as well as less common functions like sqrt, ceil, and floor and a variety of others.
The problem with these built-in functions is actually explicitly stated in the documentation: “all calculations are executed with the precision of the Java data type float“. What is not stated, unfortunately, is the implication of this seemingly innocent statement.
Implication of using float
The best way to illustrate the problem is with a simple example. Say you want to divide a given number by 100, for instance in order to move the decimal point. Easily done with the following field mapping, right?
If you execute this with an input value of 85328698, the unexpected result is 853286.94. Notice that this is not even the result you would expect if the number was simply being rounded off. And I’ll bet this is not an acceptable discrepancy for your accounting department!
I won’t get into the technical details here, but there is an excellent article at IBM DeveloperWorks explaining some of the intricacies of floating point numbers in Java.
By now you should be convinced that floats should not be used when precision matters. But when does precision matter? Definitely whenever monetary amounts are used. But I would argue that no numbers should ever lose precision in a mapping, unless this is explicitly requested.
What to do instead
Well, don’t fall into the trap of writing a User-Defined Function and just using double instead of float. This may offset the problem a little, but the double data type suffers from exactly the same problems as float.
Instead, Java provides two classes for dealing with arbitrary precision integer and decimal values in the java.math package: BigDecimal and BigInteger. These classes provide methods equivalent to many of the built-in arithmetic functions, albeit they do require you to specify exactly how you want rounding to be performed every time you perform an operation that can result in a loss of precision.
Here’s the division-by-100 example implemented using BigDecimal:
…which yields the expected result: 853286.98. Notice it doesn’t actually divide by 100, but simply moves the decimal point.
Here are a few more functions to get you going with BigDecimals:
add
String add(String a, String b) { BigDecimal bigA = new BigDecimal(a); BigDecimal bigB = new BigDecimal(b); BigDecimal bigSum = bigA.add(bigB); return bigSum.toString(); }
round
String round(String num) { BigDecimal original = new BigDecimal(num); BigDecimal rounded = original.setScale(0, BigDecimal.ROUND_HALF_UP); return rounded.toString(); }
Summary
When I say “never, ever use XI’s built-in arithmetic functions”, I mean it. The only case where their use may be justified is for statistical values where you are absolutely certain that precision is not important. But why would you be calculating that sort of thing in an XI mapping program?


Any SP's that fixes this?
I think the only way for SAP to fix this would be to introduce a second set of arithmetic and statistical functions base on BigDecimal. Because the existing functions are part of a public API, SAP risks breaking existing mappings if they change them, even though those same mappings are actually already broken 😉
Best regards,
Thorsten
did you see that:
http://help.sap.com/saphelp_nw04/helpdata/en/44/a7b4355d867455e10000000a11466f/content.htm
Regards,
michal
Thanks! I have indeed seen this. I hope to update the blog (and maybe write a new one on the new functionality) shortly, i.e. as soon as I get the time 🙂
Regards,
Thorsten
We were going to apply this setting in our environment, but first I wanted to make a mapping that shows the problem, so I can confirm that the setting change fixes it.
I cannot reproduce the float issue in a graphical mapping with the example given in this blog, nor with classic examples of 2.11 + 22.11, nor with 0.1 added to itself 20 times.
We are on PI 7.1 SPK 7. Does 7.1 always use BigDecimal perhaps?
RBL
The documentation for the standard functions in 7.1 (http://help.sap.com/saphelp_nwpi711/helpdata/en/43/c4cdfc334824478090739c04c4a249/frameset.htm) contains the wording "If the particular usage case requires more detailed results, you can also select the decimal system as the basis for the calculations. The Java class BigDecimal is used for internal purposes. For more details see SAP Note 958486." which unfortunately sheds no futher light on the situation because the note doesn't mention PI 7.1.
Perhaps someone from SAP would care to clarify this?
- Thorsten