Forum Moderators: open
I'm aware that the root of this problem is in the floating-point precision, and that it's computer and system-dependent, and I have workarounds . . . what bothers me is that dreaded word. Workaround. So when I try to do something simple like
subtotal = (form.price.value * form.quantity.value) + form.shipping.value;
if q is 3, p is 5.64, and shipping is .5, this gives the following floating support precision numbers for pre-ship subtotal and final subtotal!
16.919999999999999998 (huh? it's 16.92! )
16.919999999999999998.5
Note how shipping has been concatenated to subtotal, not added!
Here is my "workaround."
subtotal = ((form.quantity.value * 100 * form.price.value) + (form.shipping.value * 100));
subtotal = subtotal /100;
Returns 17.42 using the numbers in the previous example. This appears to do two things: by multiplying by 100 it forces the numbers to integers rounded off to currency decimals and avoids concatenation when it should be adding.
I've tried most of the usual suspects, parseFloat, evals, etc etc etc.
Point of this post: Has anyone got better, less stupid methods of dealing with this?
THE REST OF THE STORY: in reality I'm not using "form.price.value" but a loop though a numbered system, such as
for (i=0;i<num;i++) {
p = eval('form.price_'+i);
alert(p.value);
}
I know eval is depricated but I just . . . can't . . . stop . . .
I constantly have trouble with numbers in forms concatenating instead of doing the simple math. [...]subtotal = (form.price.value * form.quantity.value) + form.shipping.value;
subtotal = new Number(price * quantity) + new Number(shipping);
Though multiplying by 100 is a good idea in general. The unit of most currencies is really the smallest coin, e.g. penny for USD. If you have $1.25, really you have 125 pennies. The fixed decimal representation is just a convenience for a human reader. Some languages have a Decimal type that works for monetary calculations. But floating point is *not* a compatible stand-in for Decimal, as your example illustrates. For accuracy, use integers.
Currency is even trickier, really. Split a dollar in three: 100 / 3 is not simply 33. It's 33, 33, 34.
As above, you should store currencies as pennies (cents, whatever is the lowest value).
Imagine trying to store one third as a decimal, but you can only store 6 decimal places. You get 0.333333 (to be really precised the 3's should go on forever). Now that looks accurate, but once you start doing mathimatical calculations on an amount that is fractionally out from the real figure wanted, you can start to exagerate the amount.
In binary (as computers use), the value of 0.1 is a recurring figure and therefore cannot be stored accurately.
A short program (this used to work on the BBC Micro):
A = 0
REPEAT
A = A + 0.1
UNTIL A = 10
The above program looks simple. Keep adding 0.1 to the variable A, until A is equal to 10. It doesn't work on older computers (it may not work accurately on modern computers either - although you may need to increase the value of 10 to 1000 or 10000 if they have more accurate floating point calculations).
Why doesn't it work? Because A never EQUALS 10 do to rounding errors. It gets very, very close, but never equal.
> form.shipping.value
Whenever you take a field from a HTML document, it is by default text and should always be converted to numbers, perhaps using a method in a post above. (i.e. <input type="text"...>)
You can force the types on both sides of the + operator:
subtotal = new Number(price * quantity) + new Number(shipping);
16.919999999999999998 (huh? it's 16.92! )
As above, you should store currencies as pennies (cents, whatever is the lowest value).
Correct, which is what's happening when you take whatever's in the form and * 100. This also forces it to be a number (correct?) But still sometimes is concatenates, it's maddening!
I know the reasons for the floating point precision but don't understand why Javascript does it when it's not required. All the other languages seem to be able to figure out that 3 * 5.64 = 16.92.