HOME DOCUMENTATION DOWNLOADS BLOG

How to propely round the decimal value (amount)?

Tags: #<Tag:0x00007f65b421ad10>

At computing a resulting price (of percentage-based services) I was using a combination of Mul() and NormalRound(). The computation takes a place i a datastructure rulset

AS:NormalRound(AS:Mul(@percentage, @basePrice), 0)

@percentage and @basePrice are decimals in the db, as well as the result field

For some reason it used to work for a long time, however, suddenly it started to fail, but only in PROD environment:

 Function 'AS:NormalRound()' has failed. ---> System.FormatException: Input string was not in a correct format.
   at System.Number.ParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt)
   at System.String.System.IConvertible.ToDouble(IFormatProvider provider)
   at CZ.Advantages.Asap.Rule.NormalRoundFunction.Invoke(XsltContext xsltContext, Object[] args, XPathNavigator docContext)

Here obviously AS:Mul() didn’t fail. Mul() produces decimal output, but the way it’s not convertible to double (When I removed AS:NormalRound it started to work). It was quite hell to find out the reason since it appeared only at production and I thought it’s because of the data.

Anyway I looked at NormalRound() and found out it uses floating point (double) arithmetic, and so maybe it’s anyway wrong.

Or to use some home brew round solution (as Kit4it suggested at Number formatting (currency))

round(AS:Mul(@AmountInvoiceCurrency,@Rate) * 100) div 100

(corresponds to http://stackoverflow.com/questions/3805248/xsl-rounding-format-number-problem)
So maybe when we use AS:Div() instead of div it’s almost perfect (provided that xsl round() works well with really high/low numbers.

I think the most proper way for the time being is to use AS:AsapRound, in my case
AS:AsapRound(AS:Mul(@percentage, @basePrice), '0b58b6b8-5d68-42bd-bf23-c698a9c78cbf')

And somewhere to define the precision… It seems to me quite cumbersome.

Or am I missing something?
I would personally extend funciton AS:NormalRound so it accepts the second optional parameter ‘arithmetic’ (either ‘double’, or ‘decimal’). Or to add a new function DecimalRound.

I agree that NormalRound function uses double incorrectly. ORIGAM uses decimal type internally everywhere so this is a bug. We should fix it so only decimal is being used.

Fixed in master #4396 - NormalRound(), Abs() and Ceiling() has now decimal arithmetics

1 Like

FIxed in master #4489 - handle the exponentional format numbers as an input (e.g. 2E-12) for all AS: math xsl functions in Origam

Is this bug fixed in 2016.11.56 version?
Tahnks.

No, it was fixed only in master at that time, so it went to stable in v2018.1.