Precise Decimal Arithmetic in Profound.js
Precise decimal support was added in Profound.js 7.14.0.
To help with JavaScript decimal arithmetic issues discussed here, Profound.js provides a few solutions:
Strongly typed packed/zoned decimal fields (typically used in applications that were transformed from RPG) automatically apply rounding and truncation to JavaScript number values that are assigned to strongly typed fields. This allows most calculations with fields having less than 12 digits to work as expected using native JavaScript numbers.
Strongly typed packed/zoned fields can optionally be defined with a
precise
option. When defined this way, strongly typed field names will return a precise decimal object, instead of a native JavaScript number.The framework can be optionally configured to add the
precise
option to definitions from external sources – such as RLA database files, printer files, or Rich Display Files.Results from SQL queries can optionally be returned as
precise
decimal values.Inputs and outputs to Profound API and Low Code Modules can also be specified as
precise
.
What are Precise Decimal Values?
In Profound.js, precise decimal values are implemented using the popular decimal.js library. decimal.js
is an arbitrary precision decimal class for JavaScript that allows for precise decimal arithmetic with values having any number of digits.
Coding for precise decimal values is a lot different than for native JavaScript numbers. For example, consider the following application that uses default (not precise
) strongly typed packed decimal fields:
pjs.define("num1", { type: "packed", length: 9, decimals: 2 });
pjs.define("num2", { type: "packed", length: 9, decimals: 2 });
pjs.define("result", { type: "packed", length: 9, decimals: 2 });
num1 = 0.1;
num2 = 0.2;
result = num1 + num2;
console.log(result); // Output: 0.3
Since strongly typed packed/zoned fields return a JavaScript number by default, arithmetic is done using JavaScript operators like +
, -
, *
, and /
. And since Profound.js takes care of rounding/truncating the value to 2 decimal positions, the result is the expected value.
But, now let’s try increasing the number of digits and see what happens:
pjs.define("num1", { type: "packed", length: 19, decimals: 4 });
pjs.define("num2", { type: "packed", length: 19, decimals: 4 });
pjs.define("result", { type: "packed", length: 19, decimals: 4 });
num1 = 222222222222222.3333;
num2 = 444444444444444.2222;
result = num1 + num2;
console.log(result); // Output: 666666666666666.6
In this case the value is not as expected, because native JavaScript numbers lose precision after 15 digits.
Specifying the precise
option on the definitions will correct this, but this will also change the way that the program has to do the arithmetic operation:
pjs.define("num1", { type: "packed", length: 19, decimals: 4, precise: true });
pjs.define("num2", { type: "packed", length: 19, decimals: 4, precise: true });
pjs.define("result", { type: "packed", length: 19, decimals: 4, precise: true });
num1 = "222222222222222.3333";
num2 = "444444444444444.2222";
result = num1.plus(num2);
console.log(String(result)); // Output: 666666666666666.5555
So, now the value is correct but note the differences in the coding:
The
precise
option is added to thepjs.define()
configuration. This makes it so thatnum1
,num2
, andnum3
, refer todecimal.js
values instead of native JS numbers.The number literals are specified as strings instead of numbers, to avoid precision loss. Precise decimal values can also accept numbers, but you should strings for values with more than 15 digits.
The arithmetic coding is done with
num1.plus(num2)
instead ofnum1 + num2
.Precise decimal values are coerced to JavaScript strings, so in this case coding
num1 + num2
would result in a string222222222222222.3333444444444444444.2222
Using pjs.math to Evaluation Math Expressions
Another option for precise decimal arithmetic is to use pjs.math to evaluate a template literal string as a math expression. Precise decimal and other numeric values can be inserted into the template literal as place holders. For example:
Using Precise Decimal Without Strongly Typed Fields
Precise decimal values can be used without strongly typed fields by importing the decimal.js library and using like this:
See the decimal.js documentation for more details.