|
Yes sir.
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
Sometimes I do wish JavaScript had better types like that. I can push a precision to about 9 or 10 in JavaScript before the storable value becomes too small to be worth while. Good enough for kiddie stuff at least.
But yeah, also what Daniel said.
Jeremy Falcon
|
|
|
|
|
Javascript's innate number type is but an approximation - everybody will agree. But the serialization of those numbers in JSON makes it worse. For example, what is really meant with 0.67, 0.667, ... , 0.66666666666667 ?
Does 0.67 mean exactly 67 cents and is 0.66666666666667 to be understood as an approximation of 2/3? And does 0.667 also stand for 2/3? What does the number of decimals tell about the underlying intentions?
JSON should have a standardized notation for lossless serialization of floating point type numbers. Even C has that: the %a printf format for double in hexadecimal notation. And talking about shortcomings in JSON: please also provide a notation for bignums - both integer and rational ones.
|
|
|
|
|
This is why COBOL has a decimal type.
Your approach of using integers to represent currency will work, subject to a few caveats:
- Accounting rules require that calculations (e.g. multiplication, division) be performed with greater than 1 cent accuracy (5 decimal places, IIRC). This allows interest calculations, currency conversions etc. to work properly.
- Rounding is performed using accounting rules - round to nearest or AWAY. The difference between this and round to nearest or EVEN is when the fraction is exactly 0.5. If this case, one rounds AWAY from 0. For example, 3.145 would round to nearest or EVEN as 3.14, but round to nearest or AWAY as 3.15.
There may be other rules for accounting, but these are the major ones.
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
Daniel Pfeffer wrote: This is why COBOL has a decimal type. Never thought I'd say this about COBOL, but that's cool.
Daniel Pfeffer wrote: There may be other rules for accounting, but these are the major ones. Thanks for this man.
Jeremy Falcon
|
|
|
|
|
|
I was waiting for that. Had to give COBOL some love though.
Jeremy Falcon
|
|
|
|
|
Jeremy Falcon wrote: Had to give COBOL some love though.
Rightfully so, COBOL is better than its reputation. And still in development, latest version is COBOL-2023.
|
|
|
|
|
I remember COBOL. I worked for an accountant/programmer during grad school.
His clients used NCR COBOL machines with COBOL interpreter language.
Bullet proof.
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
Daniel Pfeffer wrote: There may be other rules for accounting
In certain domain spaces there are regulations that must be followed. And they can vary by political region.
|
|
|
|
|
.toFixed(x) should do the trick.
And maybe even +(0.1 + 0.2).toFixed(2) , which displays 0.3 just fine
A user never wants to see more than three digits anyway.
But ultimately, I do all my calculations in C# that has a decent decimal type.
I once had a customer who wanted to calculate VAT for each sales order row and then got mad the total didn't add up due to rounding errors
|
|
|
|
|
Sander Rossel wrote: .toFixed(x) should do the trick There's actually some rounding errors you'll find with that. I have a means to do the rounding, just curious to know what's peep's favorite way.
Sander Rossel wrote: But ultimately, I do all my calculations in C# that has a decent decimal type. Win for C#. This project is in 100% Node, so me no get that.
Btw, here's the routine I use to do more accurate rounding, if you want it...
const MAX_DECIMALS = 16;
export function roundOff(number: number, decimals = 0): number {
const exponent = Math.min(Math.max(decimals, 0), MAX_DECIMALS);
const factor = 10 ** exponent;
return (Math.round(((number || 0) + Number.EPSILON) * factor) || 0) / factor;
}
Sander Rossel wrote: I once had a customer who wanted to calculate VAT for each sales order row and then got mad the total didn't add up due to rounding errors Good times. Good times.
Jeremy Falcon
|
|
|
|
|
Somehow, I've never really needed this.
I consider myself a lucky man
I always wonder why JavaScript adds new stuff such as classes, let, const, and what have you, but not a decent rounding method, or an array.remove method.
Why does an array have reduce, join and flat, but not remove!? I keep googling "javascript array remove element", oh right, splice (or was it slice?), indexOf, 1
JavaScript really seems to be missing the basics, the bare necessities even, but somehow it keeps adding junk most people will never need.
|
|
|
|
|
There is a remove method for the last single element. It's called Array.prototype.pop. If you want to remove chunks of an array at a time, as you mentioned there's Array.prototype.splice. Not sure why the no bueno, just because it's called splice rather than remove .
Jeremy Falcon
modified 23-Mar-24 13:02pm.
|
|
|
|
|
Those aren't remove functions.
You forgot shift, which removes the first element.
However, I very rarely need to remove the first or last element specifically.
If I have a reference to an element I don't even know its index, I just want to be able to remove it.
It's just JavaScripts way of saying "it's an array, but you can abuse it as stack or queue."
Splice is also something different entirely.
You need an index and the number of items you want to remove starting at that index.
I can never remember it: array.splice(array.indexOf(something), 1); .
It can also be used to add new elements at the designated index, so clearly not a remove.
The slice methods just returns a portion of the array between the specified indexes, and it sounds too much like splice to be able to remember clearly which is which.
Someone new to JavaScript would never guess what it does or how to use it.
I just want to say array.remove(something); and it should remove something.
A remove function is easy to remember, easy to use and clearly conveys your intent.
I don't care if it just does array.splice(array.indexOf(something), 1); internally, I just want to be rid of that awful syntax.
Everyone who says "splice is JavaScripts remove function" is dead wrong.
|
|
|
|
|
Sander Rossel wrote: You forgot shift, which removes the first element. Didn't forget it man. It's an Internet post... not a book. I was giving a few examples. Do better than the kiddie crap on CP.
Sander Rossel wrote: I just want to say array.remove(something); and it should remove something. Well that's enough reason to hate an entire language. #sarcasm
Sander Rossel wrote: Everyone who says "splice is JavaScripts remove function" is dead wrong. Well, what I find with programmers, they lack enough maturity to not be overly emotional about crap. And they love to hate to feel intelligent or superior (usually the opposite). I'm going to give you three examples that don't require much thought, you'll probably not change your mind at all but it doesn't mean I'm wrong... bias and hate by non-experts is bias and hate after all...
const data = [1, 2, 3, 4, 5];
delete data[2];
console.log(data.filter(x => x));
console.log(data.filter((x, i) => x !== 2));
data.splice(2, 1);
console.log(data); Also, keep in mind, C# doesn't have a strong sense of immutability, like JavaScript does. Not that you can't mutate, as given in the examples...
I suggest you try running that code before perpetuating the unfounded hate.
Jeremy Falcon
|
|
|
|
|
Also, this is why I post less and less on CP. I can't post anything without someone coming along who knows little about the language and give their hateful opinion on JavaScript. It's a waste of time man, to repeat the same conversation over and over again for years.
Jeremy Falcon
|
|
|
|
|
Forgot to mention why the use of Number.EPSILON. Consider this:
1.005.toFixed(2);
Math.round(1.005 * 100) / 100;
Number.EPSILON handles these edge cases in a way that's small enough to not bother non-edge cases. Just be careful with using it though for every day calculations as it's not perfect to always rely on. But for fixing these edge cases it works great.
Jeremy Falcon
modified 21-Mar-24 12:43pm.
|
|
|
|
|
Had a similar problem before… It was a percentage breakout like VAT where they wanted it to always total to 100%. The customer demand was that you sort by original value (smallest to largest), calculate the first n-1 percentage per line item, last/biggest nth item is force set as 100-(sum (n-1 line item percentages) )
That way when you exported the values to Excel, it all balanced. Just hope the customer does not add formulas back in and notice that the biggest row is off by +/-0.01 in the calculation.
|
|
|
|
|
Jeremy Falcon wrote: complete accuracy on numbers
So, not a government project then?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Ha ha ha ha. Nope.
Jeremy Falcon
|
|
|
|
|
Okay, I'm now having flashbacks to my numerical methods class in college. One of the central points in the course was that managing precision was important, but it had to be appropriate to the calculation.
You've hit on that with currency. I remember a story where a programmer at a bank embezzled a lot of money based on harvesting roundoff error in the bank's software that he wrote. There are certifications and legal mechanisms that define how calculations are performed now. Scientific computation is similar. It's hard to imagine the analysis that the Large Hadron Collider experiments require, since the data of interest is embedded in unbelievable amounts of noise.
Heck, it's even a problem in my world of commercial ink-jet printing systems. Our internal unit of measurement is µinches (micro-inches) in a 64-bit signed integer. We deal with positioning data in 1/3600ths of an inch that can be noisy, and our resolution is 600 dots/inch. Using µinches solves a lot of scaling issues.
Software Zen: delete this;
|
|
|
|
|
This is the only correct answer: it has to be appropriate to the calculation. In some cases, perfect precision is not even mathematically possible (e.g. pi = ratio of a circle's diameter to circumference, or anything involving it like sin() or cos()).
Rational libraries are great when appropriate, but (a) they can be a lot of computing for no real world benefit, and (b) if you have any intrinsically irrational number like pi in there anywhere, their usefulness instantly becomes limited (or you have to factor out the irrationals and express the answers as ratios of them...)
So, yeah, in real life, calculate at higher than needed* precision and round to needed precision. For accounting, use appropriate accounting rounding rules for exact 0.5 values.
*what precision? Max total of your errors all added and multiplied together appropriately according to the calculations involved must be less than half of the actual final precision.
|
|
|
|
|
"floating point precision" is an oxymoron. Which is why I don't do math in JavaScript unless I don't care about rounding errors.
|
|
|
|
|
Yeah, but to be fair... this issue is with all languages.
Edit: Unless you mean the decimal type in C#? In which case... totally gotta give props to that.
Jeremy Falcon
modified 21-Mar-24 11:31am.
|
|
|
|