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 the pjs.define() configuration. This makes it so that num1, num2, and num3, refer to decimal.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 of num1 + num2.

    • Precise decimal values are coerced to JavaScript strings, so in this case coding num1 + num2 would result in a string 222222222222222.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.