This extension is an interface to the basic calculator function for Arbitrary Precision Mathematics.
1 Overcoming Javascript numeric precision issues
Floatingpoint numbers are represented as binary (base 2) fractions. Regrettably, most decimal fractions cannot be represented exactly as binary fractions. The decimal floatingpoint numbers you enter are only approximated by the binary floatingpoint numbers actually stored in the machine. That being said, you'll see that floatingpoint arithmetic is NOT 100% accurate.
0.2 + 0.1 0.30000000000000004 0.3  0.1 0.19999999999999998 1111.11+1111.11+1111.11+1111.11+1111.11 5555.549999999999
To solve this problem, we need an alternative to Javascript numbers. That's why we have a BigDecimal
Number Object and library, to address this issue.
It provides arbitraryprecision decimal arithmetic.
1.1 Decimal data type
Primitive numeric types are useful for storing single values in memory. But when dealing with calculation using double and float types, there is a problems with the rounding. It happens because memory representation doesn't map exactly to the value.
To solve this issues you can use immutable, arbitraryprecision signed decimal numbers.
A BigDecimal
consists of an arbitrary precision integer unscaled value and a 32bit integer scale. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. The value of the number represented by the BigDecimal
is therefore (unscaledValue × 10scale).
The BigDecimal
class provides operations for arithmetic, scale manipulation, rounding, comparison, hashing, and format conversion. The toString() method provides a canonical representation of a BigDecimal
.
1.2 Decimals created by application
In the following example we can see two implementations of the factorial function.
 First uses the native JavaScript numbers. We can see data precision is lost whe number increases.

Second used multiplications using
Ax.math.bc.mul
function that takes two numnbers and returns a Decimal data type as result.
<script> function factorialDouble(num) { return (num === 0) ? 1 : num * factorialDouble( num  1 ) } function factorialDecimal(num) { return (num === 0) ? 1 : Ax.math.bc.mul(num, factorialDecimal( num  1 )) } console.log(Ax.text.String.format("%3s %24s %s", "Num", "double", "Decimal")); for (var idx = 1; idx <= 50; idx++) console.log(Ax.text.String.format("%3s %24s %s", idx, factorialDouble(idx), factorialDecimal(idx))); </script>
2 Decimal returned from database
Databases have a special type called DECIMAL
to perform abitrary precision
calculations. When returning data of such type from database to application,
data is mapped to class BigDecimal
.
2.1 Getting count(*)
A typical operation that returns a BigDecimal
in most databases is when selecting a COUNT(*).
 executeGet, automatically detects for
BigDecimal
and converts to int/long  executeGetInt, returns an int
 executeGetLong, returns a long
Notice that the previous functions prevents the need to use BigDecimal
comparision
to test for COUNT(*).
2.2 Dealing with bigdecimals
<script> var row = Ax.db.executeQuery(` SELECT CAST(0.02 AS DECIMAL) a, CAST(0.03 AS DECIMAL) b FROM systables WHERE tabid = 1 `).toOne(); // A: using javascript double precision aritmetic console.log("Double precision"); console.log(0.02  0.03); console.log(row.a  row.b); // B: using BigDecimal aritmetic console.log("BigDecimal precision"); console.log(new Ax.sql.Decimal("0.02").subtract(new Ax.sql.Decimal("0.03"))); console.log(row.a.subtract(row.b)); </script>
Notice that Decimal types returned from database must be operated using its own function (add, subtract, div, mul, etc). If not, they are converted to double before operating.
3 BC functions
The Ax.math.bc package contains a group of functions to perform BigDecimal
operations.
Return  Method  Description 

Creation  
BigDecimal

of(String number)  Translates the string representation of a BigDecimal into a BigDecimal . The string representation consists of an optional sign, '+' ( '\u002B') or '' ('\u002D'), followed by a sequence of zero or more decimal digits ("the integer"), optionally followed by a fraction, optionally followed by an exponent. 
BigDecimal

of(Number number)  Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floatingpoint value. The scale of the returned BigDecimal is the smallest value such that (10scale × val) is an integer. 
add, sub, mul, div  
BigDecimal

add(Number n1, Number n2, ...)  Returns a BigDecimal whose value is (this + augend), and whose scale is max(this.scale(), augend.scale()). 
BigDecimal

sub(Number n1, Number n2, ...)  Returns a BigDecimal whose value is (this  subtrahend), and whose scale is max(this.scale(), subtrahend.scale()). 
BigDecimal

mul(Number n1, Number n2, ...)  Returns a BigDecimal whose value is (this × multiplicand), and whose scale is (this.scale() + multiplicand.scale()). 
BigDecimal

div(Number n1, Number n2)  Returns a BigDecimal whose value is (this / divisor), and whose preferred scale is (this.scale()  divisor.scale()); if the exact quotient cannot be represented (because it has a nonterminating decimal expansion) an ArithmeticException is thrown. 
BigDecimal

div(Number n1, Number n2, int scale)  Returns a BigDecimal whose value is (this / divisor), and whose preferred scale is (this.scale()  divisor.scale()); if the exact quotient cannot be represented (because it has a nonterminating decimal expansion) an ArithmeticException is thrown. 
BigDecimal

div(Number n1, Number n2, int scale, String roundingMode)  Returns a BigDecimal whose value is (this / divisor), and whose preferred scale is (this.scale()  divisor.scale()); if the exact quotient cannot be represented (because it has a nonterminating decimal expansion) an ArithmeticException is thrown. 
abs, pow, round, scale  
BigDecimal

abs(Number n1)  Returns a BigDecimal whose value is the absolute value of this BigDecimal , and whose scale is this.scale(). 
BigDecimal

pow(Number n1, int n2)  Returns a BigDecimal whose value is (thisn), The power is computed exactly, to unlimited precision. 
BigDecimal

round(Number n1, int precision)  Returns a BigDecimal rounded according to the MathContext settings. If the precision setting is 0 then no rounding takes place 
BigDecimal

scale(Number n1, int scale, int roundingMode)  Returns a BigDecimal whose scale is the specified value, and whose value is numerically equal to this BigDecimal 's.
Throws an ArithmeticException if this is not possible.
This call is typically used to increase the scale, in which case it is guaranteed that there exists a 
int  getScale(Number n1)  Returns the scale of this BigDecimal . If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. For example, a scale of 3 means the unscaled value is multiplied by 1000. 
comparision  
boolean  isZero(Number n1)  Returns a comparision between the number and BigDecimal .ZERO. 
boolean  equals(Number n1, Number n2)  Compares this BigDecimal with the specified Object for equality. Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method). 
int  compareTo(Number n1, Number n2)  Compares this BigDecimal with the specified BigDecimal. Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. This method is provided in preference to individual methods for each of the six boolean comparison operators (<, ==, >, >=, !=, <=). The suggested idiom for performing these comparisons is: (x.compareTo(y) <op> 0), where <op> is one of the six comparison operators. 
<script> console.log(Ax.math.bc.add(2, 2)); console.log(Ax.math.bc.add(2, 2, 3)); console.log(Ax.math.bc.mul(2, 2)); console.log(Ax.math.bc.mul(2, 2, 3)); </script>
3.1 Comparing numbers
Be carefull when comparing BigDecimal
to a javascript number. You can use
 equals  takes scale into account
 compareTo  does not take scale but returns 0, 1 or 1 accoding if number is equal, greater or lesser
 isZero  compares if number is zero
equals
Expression  Result 

Ax.math.bc.equals(new Ax.math.BigDecimal (0), 0.00) 
false

Ax.math.bc.equals(new Ax.math.BigDecimal (0.0), 0.00) 
false

Ax.math.bc.equals(new Ax.math.BigDecimal (0.00), 0.00) 
false

Ax.math.bc.equals(new Ax.math.BigDecimal (0), 0) 
true 
Ax.math.bc.equals(new Ax.math.BigDecimal (0.00), new Ax.math.BigDecimal (0.00)) 
true 
Why the first cases fails ?
You are passing 0.00 as right side element. This value, a javascript double, is passed
to the equals function and it creates a BigDecimal
using the double toString()
representation that will generate it.
Unlike compareTo, equals considers two BigDecimal
objects equal only if they are equal
in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).
compareTo
Expression  Result 

Ax.math.bc.compareTo(new Ax.math.BigDecimal (0), 0.00) 
0 
Ax.math.bc.compareTo(new Ax.math.BigDecimal (0.0), 0.00) 
0 
Ax.math.bc.compareTo(new Ax.math.BigDecimal (0.00), 0.00) 
0 
Ax.math.bc.compareTo(new Ax.math.BigDecimal (1), 0.00) 
1 
Ax.math.bc.compareTo(new Ax.math.BigDecimal (0), 1.00) 
1 
isZero
Expression  Result 

return Ax.math.bc.isZero(new Ax.math.BigDecimal (0)) 
true 
return Ax.math.bc.isZero(new Ax.math.BigDecimal (0.00)) 
true 
4 BigDecimal tutorial
4.1 Primer on Financial Issues
Currency calculations require precision to a specific degree, such as two digits after the decimal for most currencies. They also require a specific type of rounding behavior, such as always rounding up in the case of taxes.
4.1.1 Tax example
For example, suppose we have a product which costs 10.00 in a given currency and the local sales tax is 0.0825, or 8.25%. If we work it out on paper, the tax amount is,
10.00 * 0.0825 = 0.825
Because our precision for the currency is two digits after the decimal, we need to round the 0.825 figure. Also, because this is a tax, it is good practice to always round up to the next highest cent. That way when the accounts are balanced at the end of the day, we never find ourselves underpaying taxes.
0.825 > 0.83
And so the total we charge to the customer is 10.83 in the local currency and pay 0.83 to the tax collector. Note that if we sold 1000 of these, we would have overpaid the collector by this much,
1000 * (0.83  0.825) = 5.00
4.1.2 Quantity example
Another important issue is where to do the rounding in a given computation. Suppose we sold Liquid Nitrogen at 0.528361 per liter. A customer comes in and buys 100.00 liters, so we write out the total price,
100.0 * 0.528361 = 52.8361
Because this isn't a tax, we can round this either up or down at our discretion. Suppose we round according to standard rounding rules: If the next significant digit is less than 5, then round down. Otherwise round up. This gives us a figure of 52.84 for the final price.
Now suppose we want to give a promotional discount of 5% off the entire purchase. Do we apply this discount on the 52.8361 figure or the 52.84 figure? What's the difference?
Calculation 1: 52.8361 * 0.95 = 50.194295 = 50.19 Calculation 2: 52.84 * 0.95 = 50.198 = 50.20
Note that we rounded the final figure by using the standard rounding rule.
See how there's a difference of one cent between the two figures? The old code never bothered to consider rounding, so it always did computations as in Calculation 1. But in the new code we always round before applying promotions, taxes, and so on, just like in Calculation 2. This is one of the main reasons for the one cent error.
4.2 Introducing BigDecimal
From the examples in the previous section, it should be clear that we need two things:
 Ability to specify a scale, which represents the number of digits after the decimal place
 Ability to specify a rounding method
4.2.1 Creating a BigDecimal
You can get a BigDecimal
from a double, however it is a good idea to use the string constructor. Let's
see why.
<script> console.log(Ax.math.bc.of("1.4")); console.log(Ax.math.bc.of(1.4)); </script>
So we need to be carefull when using Javascript numbers in our operations if they are related to the previous issues.
4.2.2 Rounding and Scaling
To set the number of digits after the decimal, use the setScale(scale) method.
However, it is good practice to also specify the rounding mode along with the scale by using
setScale(scale, roundingMode)
. The rounding mode specifies how to round the number.
Why do we also want to specify the rounding mode? Let's use the BD of 1.5 from above as an example,
<script> var bd = new Ax.math.BigDecimal(1.4); bd.setScale(1); // This is an incorrect syntax of setScale() </script>
It throws the exception because it does not know how to round 1.399999 ....
So it is a good idea to always use setScale(scale, roundingMode)
.
There are eight choices for rounding mode:
<script> console.log(Ax.math.bc.RoundingMode); console.log(""); var bd = Ax.math.bc.of(1.4); console.log("Number original =" + bd); console.log("Number scaled 4=" + bd.setScale(4, "HALF_UP")); console.log("Number scaled 18=" + bd.setScale(18, "HALF_UP")); </script>
<script> function round(bd, rounding) { var res = bd.setScale(2, rounding); console.log(Ax.text.String.format("%6s %6s %s", bd, res,rounding)); return res; } for (var mode of Ax.math.bc.RoundingMode) { if (mode == Ax.math.bc.RoundingMode.UNNECESSARY) continue; round(Ax.math.bc.of("0.333"), mode); round(Ax.math.bc.of("0.333"), mode); } </script>
The following table shows results of scaling a value to tow deciml places on each mode
Mode  Description  0.333  0.333 

CEILING  Ceiling function  0.34  0.33 
UP  ..  0.34  0.34 
DOWN  Round towards zero  0.33  0.33 
FLOOR  Floor function  0.33  0.34 
HALF_UP  Round up if decimal >= .5  0.33  0.33 
HALF_DOWN  Round up if decimal > .5  0.33  0.33 
HALF_EVEN  Round half even will round as normal. However, when the rounding digit is 5, it will round down if the digit to the left of the 5 is even and up otherwise  0.33  0.33 
UNNECESSARY  When you need to use one of the methods that requires input of a rounding mode but you know the result won't need to be rounded.  RoundingNeccesary  RoundingNeccesary 
4.3 BigDecimal Immutability
BigDecimal
numbers are immutable. What that means is that if you create a new BD with value "2.00",
that object will remain "2.00" and can never be changed.
So how do we do math then?
The methods .add(), .multiply(), and so on all return a new BigDecimal
instance with new value containing the result.
For example, when you want to keep a running total of the order amount,
<script> var amount = new Ax.math.BigDecimal("2"); var other = new Ax.math.BigDecimal("3"); amount = Ax.math.bc.add(amount, other); console.log(amount); </script>
4.4 Arithmetic and precission
Some considerations about precission should be taken when using BigDecimal
arithmetic operations.
 In divide operation (div), by default value returned has a minimum scale of 16 decimals. If operands has no scale defined (for example using strings or float native classes), a scale of 16 decimals is used to avoid exception in operations returning period decimal.
 In multiply operation (mul) scales of both operads are added to get the result decimal precission.
<script> var div_floats = Ax.math.bc.div(1, 3); console.log(div_floats); var div_scaled = Ax.math.bc.div(new Ax.math.BigDecimal(1).setScale(4), new Ax.math.BigDecimal(3).setScale(4)); console.log(div_scaled); var mul_scaled = Ax.math.bc.mul("0", new Ax.math.BigDecimal(2.15).setScale(8, Ax.math.bc.RoundingMode.HALF_UP)); console.log(mul_scaled); </script>
You cannot know in advance the real precission of float numbers. That's why it's recommended to use always create bigdecimals from string representation of numbers.
<script> var d1 = Ax.math.bc.of(1.23); var d2 = Ax.math.bc.of(2.15); console.log("d1=" + d1); console.log("d2=" + d2); var mul_float = Ax.math.bc.mul(d1, d2); console.log("result=" + mul_float); console.log("============================"); var d3 = Ax.math.bc.of("1.23"); var d4 = Ax.math.bc.of("2.15"); console.log("d3=" + d3); console.log("d4=" + d4); var mul_scaled = Ax.math.bc.mul(d3, d4); console.log("result=" + mul_scaled); </script>
5 Nashorn vs Graal script engines
Nashorn and Graal have some differences when treating BigDecimal
comparisons.
 Nashorn, succesfully compares
BigDecimal
to int values  Graal, fails to compare
BigDecimal
to int values
Operation  Nashorn  Graal 

BigDecimal (0) == 0 
true  false 
BigDecimal (1) == 1 
true  false 
It's better to use always BigDecimal
comparison functions to ensure
application runs in both engines.