Page 4 of 5

Re: another (different) rounding problem

PostPosted: Thu Sep 21, 2017 2:00 pm
by lwjwillemsen
Mathematical rounding should always be correct in my opinion


That's the book all about:

A computer can not do true mathematical rounding due to the representation of numbers in a series of bits / bytes.

Re: another (different) rounding problem

PostPosted: Thu Sep 21, 2017 2:46 pm
by jcompagner
lwjwillemsen wrote:
Mathematical rounding should always be correct in my opinion


That's the book all about:

A computer can not do true mathematical rounding due to the representation of numbers in a series of bits / bytes.


exactly

i was already really confused about that statement..
Because there is no such thing a "Mathematical rounding" if you work with floating points notation numbers!!
you shouldn't really round
you only round because of " financial administration challenges."

if you calculate stuff like summing/dividing/multiply you name it, you should never round there, only the end result if " financial administration" requires that.

This should be really clear to everybody here, that rounding in javascript is NOT possible, you never are guaranteed that you get exactly that number (with 2 decimals)

looking at harjo's number:
4.725

if you round that in the few ways that are represented here then you get
4.72 righ? that is because it is very likely 4.724999999 (because 4.725 can be expressed exactly like that) where you start with or that 4.73 can't be exactly be shown like that and you really get 4.729999999999
thats why that +0.000001 works
but still you dont really have 4.73 ... what you really have is 4.73000001

That you then store that in a database of NUMERIC(10,2) that will delete the 00001 from it, but when you read it back it it can be that Servoy really reads it as BigDecimals (a real Numeric represenation class)
but at the moment you touch it in js it will be a floating point again and it will have something after it..

Re: another (different) rounding problem

PostPosted: Thu Sep 21, 2017 3:19 pm
by omar
I decided to do some rigorous tests on the different rounding methods above to get to the bottom of this and literally found thousands of rounding errors.

For example Marcs routine fails when rounding 1/201 (equals 0.004975124378109453) to 2 decimals returning 0.01 instead of 0.00. Another: 0.146 with precision 1 rounds to 0.2 etc.

Roberts routine fails when rounding 1/6667 (equals 0.000149992500037498125) to 4 decimals returning 0.0002 instead of 0.0001. Another: 0.206 with precision 8 returns 0.20600001 etc.

I tested using two methods: 1/x (1 divided by x) and composing decimal numbers by string concatenation. Let me know if you would like me to post the code for that.

The only rounding method that I have not found any errors in is the one I posted earlier. I guess nobody tested it because they figured it was to short to be able to work correctly ;-)

So until a better method comes along the best way is: (credit for the core of this solution goes to Jack Moore. See: http://www.jacklmoore.com/notes/rounding-in-javascript/)

Code: Select all
/**
* Round using exponential notation shifting
*
* @param {Number} value
* @param {Number} precision
*/
function round(value, precision) {
   return Number(Math.round(Number(Math.abs(value)+'e'+precision))+'e-'+precision) * (value < 0 ? -1 : 1)
}

Re: another (different) rounding problem

PostPosted: Thu Sep 21, 2017 4:58 pm
by ROCLASI
Nice!
I missed that one.

Re: another (different) rounding problem

PostPosted: Fri Sep 22, 2017 9:37 am
by jcompagner
omar wrote:
Code: Select all
/**
* Round using exponential notation shifting
*
* @param {Number} value
* @param {Number} precision
*/
function round(value, precision) {
   return Number(Math.round(Number(Math.abs(value)+'e'+precision))+'e-'+precision) * (value < 0 ? -1 : 1)
}



interesting that that works, it is for sure a bit slower as all the others ;)
because its quite a lot number -> to string (concat) -> number -> to string -> tonumber

So the magic here has to be in the Number constructor and the parse of the string that that constructor gets, that weird rounding errors won't happen..

Re: another (different) rounding problem

PostPosted: Fri Sep 22, 2017 9:47 am
by omar
If you leave out the second Number constructor it works as well but you will get an implicit conversion anyway. I included it myself to get rid of the Servoy warning that otherwise occurs. I like to keep my solutions completely free of warnings ;-) Performance is not that bad either, during testing I did several hundred millions of round operations in a few seconds.

Re: another (different) rounding problem

PostPosted: Fri Sep 22, 2017 10:14 am
by patrick
We will add that to svyUtils/svyJSUtils so it doesn't get lost.

Re: another (different) rounding problem

PostPosted: Thu Apr 05, 2018 1:08 pm
by Harjo
The story continues:

please do provide this to this method:

round(42.50/100*21)

it wil return 8.92 instead of 8.93 :shock: :shock:

in command console you see that if you do this: =>42.50/100*21
will return: 8.924999999999999


Code: Select all
/**
* Round using exponential notation shifting
*
* @param {Number} value
* @param {Number} precision
*/
function round(value, precision) {
   return Number(Math.round(Number(Math.abs(value)+'e'+precision))+'e-'+precision) * (value < 0 ? -1 : 1)
}


So also this function, is NOT failproof

Re: another (different) rounding problem

PostPosted: Thu Apr 05, 2018 2:09 pm
by omar
Hi Harjo,

That's debatable, imho. The correct answer for rounding 8.924999999999999 is actually 8.92... The problem is that when you calculate 42.50/100*21 Javascript probably has to calculate the answer in two steps and use implicit rounding which we already know is unsuitable for the job. A possible solution is to multiply the inputs that contain decimals by 100 and divide with 100 after obtaining the answer: (4250/100*21)/100 = 8.925 which then correctly rounds to 8.93. Multiplying first also helps: 42.50*21/100 = 8.925.

Re: another (different) rounding problem

PostPosted: Thu Apr 05, 2018 3:41 pm
by Harjo
your suggestion, is just again a workaound, which you have to think about.
I'm looking for a 100% accurate solution, which always works and calculates the right way.

so far, my simple function, works always in ALL examples above:

Code: Select all
function core_round_money(vValue) {
   if(vValue<0) {
      return Math.round((vValue - 0.00000001) * 100) / 100;
   } else {
      return Math.round((vValue + 0.00000001) * 100) / 100;
   }
}

Re: another (different) rounding problem

PostPosted: Thu Apr 05, 2018 4:07 pm
by omar
So 0.9249999 = 0.92 and 0.92499999 = 0.93? That doesn't make sense to me, sorry.

Re: another (different) rounding problem

PostPosted: Thu Apr 05, 2018 4:19 pm
by Harjo
the result is what counts for me:
with this method:

Code: Select all
/**
* Round using exponential notation shifting
*
* @param {Number} value
* @param {Number} precision
*/
function round(value, precision) {
   return Number(Math.round(Number(Math.abs(value)+'e'+precision))+'e-'+precision) * (value < 0 ? -1 : 1)
}



round(42.50/100*21) will return 8.92 instead of 8.93

in my case:

Code: Select all
function core_round_money(vValue) {
   if(vValue<0) {
      return Math.round((vValue - 0.00000001) * 100) / 100;
   } else {
      return Math.round((vValue + 0.00000001) * 100) / 100;
   }
}


core_round_money(42.50/100*21) will return correctly 8.93

Re: another (different) rounding problem

PostPosted: Thu Apr 05, 2018 4:28 pm
by lwjwillemsen
I think that in the business (non scientific) area the rounding method of Harjo will do fine.
It's all about the number of significant digits you want or require...

Nice one for Servoy Camp Harjo :wink:

Re: another (different) rounding problem

PostPosted: Fri Apr 13, 2018 5:07 pm
by ROCLASI
Sorry, couldn't help myself.

Must.be.a.oneliner.... ;)


Code: Select all
/**
* @param {Number} value
* @param {Number} precision
*/
function roundNumber(value, precision ) {
    return Math.round( (value + (value < 0 ? -0.00000001 : 0.00000001)) * Math.pow(10, precision)) / Math.pow(10, precision);
}

Re: another (different) rounding problem

PostPosted: Fri Apr 13, 2018 5:12 pm
by mboegem
:lol: :lol: :lol:

You must be a fan of minimised js files... all code in 1 line :wink:
Code: Select all
function roundNumber(value,precision){return Math.round((value+(value<0?-1e-8:1e-8))*Math.pow(10,precision))/Math.pow(10,precision)}