This temporary fork of Michael Kay's Saxon is just a show case of using Saxon in the e-commerce domain requiring best numeric accuracy.
After convincing CEN TC 434 WG1 to add decimal-based floating-point-support as recommendation of the EU e-invoice standard (EN16931), this project aims to enhance the EN16031 XSLT Schematron validation reference implementation with the support of decimal-based floating-point.
Decimal-based floating-point was invented for the commercial sector. It missed the early IEEE 754 standard in the late 80ths and still took 20 years until it was embraced by IEEE 754 in 2008. Now being part of all major libraries as Java, .Net, Intel, etc.
quantity = 1000000000.0
priceAmount = 1.0
baseQuantity = 3
$quantity * ($priceAmount div $baseQuantity)) = (1000000000.0 *(1.0 div 3 )) = 333333333.333333333333333333
($quantity * $priceAmount div $baseQuantity) = (1000000000.0 * 1.0 div 3 ) = 333333333.3333333333333333333333333333333333
The above values should be the same, but differ by 0.0000000000000003333333333333333. In the energy & pharma sector prices with 6 to 9 decimal places are often and also going along with high quantities. By this, these errors show-up easily on Cent level.
$quantity * ($priceAmount div $baseQuantity)) = (1000000000.0 *(1.0 div 3 )) = 333333333.3333333333333333333333333
($quantity * $priceAmount div $baseQuantity) = (1000000000.0 * 1.0 div 3 ) = 333333333.3333333333333333333333333
- http://speleotrove.com/decimal/decifaq.html
- https://github.com/svanteschubert/DecimalFloatingPointExample
- https://dzone.com/articles/never-use-float-and-double-for-monetary-calculation
- https://blogs.oracle.com/corejavatechtips/the-need-for-bigdecimal
This Saxon update is achieved by several minor enhancements:
- Using solely decimal-based floating-point instead of binary floating-point. The fix was to disable Double creation in NumericValue.
- Extending the existing BigDecimal implementation to full floating-point support.
- Adding highest Java precision decimal-based floating-point support to multiplication and division of BigDecimals.
- Added round-half-up() function as integrated extension functions of SAXON, as half-up rounding is the default rounding in EU e-commerce - the rounding that we had learned in school - and now also added as default rounding to the EN16931 specification. The W3C XPath round() function is different by always rounding in the direction of positives, e.g. -1.5 becomes -1.
As the Saxon HE sources do not exist on GitHub, I downloaded the sources and the pom.xml from the Maven Repository into a Maven directory structure. To make the JAR become useable, further artifacts had to be copied from the published SAXON JAR:
- META-INF folder - (but removing all signature information)
- src/main/resources/net/sf/saxon/data/
I have added a smoke test case to ease debugging from the IDE. The output XML file will be generated as target/generated-sources/out.xml file. JDK 1.8 is required by the original Saxon of Saxonica and Maven as build environment. Build & smoke test can be executed via command-line by calling: mvn clean install
There is bash script 'saxon-update.sh', which download the latest Saxon from Maven and rebase our changes on top of it. Two variables of next & current version of Saxon needs to be adopted.
- accuracy-feature (main branch)
Contains the script and latest changes should be worked here. - saxon-upstream
The required parts of the Maven Saxon sources and binaries JAR are being added on top of this branch. - SAXON-HE-v<VERSION>
Branch with original Saxon functionality. Extends the saxon-upstream of Saxon source & binary JARs with the Saxon pom.xml from Maven. Including commits to allow the Saxon sources to be able to build. - SAXON-HE-accuracy-v<VERSION>
Previous version of our functionality based on a previous SAXON version. - prototyping
Initial work before it was later refactored to be automated by bash script 'saxon-update.sh'. - basics
Branch the inital bash script was being started.