Forum Moderators: open

Message Too Old, No Replies

Getting Numbers to Stay Numbers

What to do when they just want to stay as strings

         

rocknbil

6:49 pm on Mar 29, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I constantly have trouble with numbers in forms concatenating instead of doing the simple math. I only have this problem with Javascript, no other language.

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 . . .

jollymcfats

9:21 pm on Mar 29, 2005 (gmt 0)

10+ Year Member



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;

You can force the types on both sides of the + operator:

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.

Bernard Marx

7:23 am on Mar 30, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Don't bother with the 'new'. This creates Number type objects (which are then evaluated as primitives when added, so it's no real worry here).

subtotal = Number(price * quantity) + Number(shipping);

PCInk

8:03 am on Mar 30, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



> 16.919999999999999998 (huh? it's 16.92! )

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"...>)

rocknbil

5:52 pm on Mar 30, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



A few responses:

You can force the types on both sides of the + operator:
subtotal = new Number(price * quantity) + new Number(shipping);

Believe me, I have tried that exactly as you have it there and at times I still get a concatenation. I've tried parseFloat, parseInt, Number, both sides of equation, nesting and sub-nesting till I'm blind stupid . . . can't even remember them all.

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.