.1 plus .1 equals what??
June 30, 2008 8:17 PM Subscribe
How best to circumvent the limitations of floating point arithmetic in Javascript?
I'm working on an application where I need numbers to be exact and stay exact over possibly many operations, or as exact as a floating point number can be, and it would be most convenient for me to do all those operations in Javascript. However, Javascript doesn't have any built-in libraries for more accurate math and their floating point operations are notoriously prone to rounding problems even after one simple operation. I figured there would be some sort of obvious thing that everyone does to make floating point math do the right thing in Javascript, but I'm beginning to doubt that. My google-fu has turned up no consensus, and all the things I've found or thought of are kind of terrible hacks. While I doubt that you guys will think of anything I haven't come across already, I'd love to hear experiences, tips or tricks that worked for you.
I'm working on an application where I need numbers to be exact and stay exact over possibly many operations, or as exact as a floating point number can be, and it would be most convenient for me to do all those operations in Javascript. However, Javascript doesn't have any built-in libraries for more accurate math and their floating point operations are notoriously prone to rounding problems even after one simple operation. I figured there would be some sort of obvious thing that everyone does to make floating point math do the right thing in Javascript, but I'm beginning to doubt that. My google-fu has turned up no consensus, and all the things I've found or thought of are kind of terrible hacks. While I doubt that you guys will think of anything I haven't come across already, I'd love to hear experiences, tips or tricks that worked for you.
It might be easier to answer this question if you told us the reason why you need accurate floating point math. If you're doing financial calculations, Javascript is probably the wrong approach as it runs entirely on the client-side and is completely insecure. If you're doing some complex calculations and getting inaccurate results, there's probably a way to redesign your algorithm so it works better with binary floating point numbers. For any other reason I can think of, it'd be far easier to do the real calculations on the server-side and use the client side for show numbers.
Fixed point math is feasible in Javascript. That's just storing integers where you know where the decimal should go -- i.e. 583 would represent $5.83. Writing an accurate floating point library would probably be too big an undertaking to be worthwhile, unless you've got a good reason.
posted by zixyer at 8:33 PM on June 30, 2008
Fixed point math is feasible in Javascript. That's just storing integers where you know where the decimal should go -- i.e. 583 would represent $5.83. Writing an accurate floating point library would probably be too big an undertaking to be worthwhile, unless you've got a good reason.
posted by zixyer at 8:33 PM on June 30, 2008
In my experience, people who want floating point math to "do the right thing" don't understand floating point math. 0.1 doesn't have an exact representation in floating point. Adding two numbers that don't have exact representations won't get you an exact representation. That's not the way it works.
JavaScript uses IEEE-754 double precision floating point. If you're dealing with, say, financial numbers, don't use floating point. Do your addition/subtraction cents (or millicents), and convert for display.
posted by dws at 8:34 PM on June 30, 2008
JavaScript uses IEEE-754 double precision floating point. If you're dealing with, say, financial numbers, don't use floating point. Do your addition/subtraction cents (or millicents), and convert for display.
posted by dws at 8:34 PM on June 30, 2008
Here is a nice thread on doing floating point operations using javascript : ala WebMasterWorld
One of the more interesting solutions is posted by Fotiman and involves splitting the decimal off and doing math on it separately. Still not an idea solution, but at least its something interesting to look at.;
posted by ibfrog at 8:37 PM on June 30, 2008
One of the more interesting solutions is posted by Fotiman and involves splitting the decimal off and doing math on it separately. Still not an idea solution, but at least its something interesting to look at.;
posted by ibfrog at 8:37 PM on June 30, 2008
Sounds like you want to do fixed-point math.
posted by zsazsa at 8:43 PM on June 30, 2008 [1 favorite]
posted by zsazsa at 8:43 PM on June 30, 2008 [1 favorite]
Your question can't be answered without an understanding of what you're trying to do. Unless you understand your own problem a bit better, you won't be able to tell whether what you need is fixed point, rational arithmetic, interval arithmetic, some sort of bignum, using a more numerically stable algorithm, or just truncating values for display.
AFAIK JavaScript's floating point is just as good as any other language's — it's generally using the machine's native IEEE floating point anyway. Floating point numbers are not real numbers and they do not behave like reals, although most of the time you can get by pretending that they do. What are the "notorious rounding problems" you're talking about?
posted by hattifattener at 9:06 PM on June 30, 2008
AFAIK JavaScript's floating point is just as good as any other language's — it's generally using the machine's native IEEE floating point anyway. Floating point numbers are not real numbers and they do not behave like reals, although most of the time you can get by pretending that they do. What are the "notorious rounding problems" you're talking about?
posted by hattifattener at 9:06 PM on June 30, 2008
I need numbers to be exact and stay exact over possibly many operations, or as exact as a floating point number can be, and it would be most convenient for me to do all those operations in Javascript. However, Javascript doesn't have any built-in libraries for more accurate math and their floating point operations are notoriously prone to rounding problems even after one simple operation.
Fixed that for you.
It isn't Javascript's fault.
posted by flabdablet at 9:06 PM on June 30, 2008
Fixed that for you.
It isn't Javascript's fault.
posted by flabdablet at 9:06 PM on June 30, 2008
Best answer: Javascript is not good for this, use something else. Since you are using .js anyway, you probably already know how to make AJAX calls and so you can have something on the server side do your reliable calculations instead of trying to make a square peg fit a round hole.
posted by rhizome at 9:26 PM on June 30, 2008
posted by rhizome at 9:26 PM on June 30, 2008
You probably already know this, but the only bit of advice I'd give using Java (probably applies to javascript as well) is not to forget the must-dos like writing a custom equality check and using it EVERYWHERE you would otherwise use equals, less than or greater than.
For example:
posted by true at 9:33 PM on June 30, 2008
For example:
A less than Bshould be written as
"!equals(A,B) AND A less than B"where equals(A,B) is defined as
ABS_VALUE(A-B) less than EPSILONwhere EPSILON is some very very small number.
posted by true at 9:33 PM on June 30, 2008
Best answer: Sounds like you want to do fixed-point math.
Or, potentially, arbitrary precision arithmetic.
posted by weston at 9:43 PM on June 30, 2008
Or, potentially, arbitrary precision arithmetic.
posted by weston at 9:43 PM on June 30, 2008
Response by poster: Thanks, ibfrog, I hadn't found that one before.
dws et al, I'm well aware of how floating point math works, although I appreciate that you'd like to educate me. I'm just pining for types like BigDecimal that make working with decimal numbers less prone to answers that disagree with actual math. I mean, there is a true "right thing", even if it can't be represented in floating point. I want that number, or as close as I can get.
I guess I should have specified decimal arithmetic instead of floating point arithmetic. I thought it was clear what I was talking about - didn't mean it to sound like I thought it was unique to Javascript.
posted by crinklebat at 10:03 PM on June 30, 2008
dws et al, I'm well aware of how floating point math works, although I appreciate that you'd like to educate me. I'm just pining for types like BigDecimal that make working with decimal numbers less prone to answers that disagree with actual math. I mean, there is a true "right thing", even if it can't be represented in floating point. I want that number, or as close as I can get.
I guess I should have specified decimal arithmetic instead of floating point arithmetic. I thought it was clear what I was talking about - didn't mean it to sound like I thought it was unique to Javascript.
posted by crinklebat at 10:03 PM on June 30, 2008
AFAIK JavaScript's floating point is just as good as any other language's — it's generally using the machine's native IEEE floating point anyway. Floating point numbers are not real numbers and they do not behave like reals
Technically speaking, all floating point numbers are real numbers, but not all real numbers are floating point numbers.
posted by delmoi at 10:34 PM on June 30, 2008
Technically speaking, all floating point numbers are real numbers, but not all real numbers are floating point numbers.
posted by delmoi at 10:34 PM on June 30, 2008
Technically speaking, all floating point numbers are real numbers, but not all real numbers are floating point numbers.
Well, sure, in the same way that integers are real numbers. I believe what hattifattener meant is: Computer operations on floating point numbers may produce results which do not match the results of manual arithmetic on real numbers. For example, (0.1 + 0.1) - 0.2 = 0 but (0.1 + 0.1 + 0.1) - 0.3 = 5.5511e-017; (2^100+2^10)-2^100 = 0, and so on.
Of course, you have to be dealing with pretty high precision numbers for floating point error to be a real concern; the 54 bits of a double-precision float can represent the distance from the earth to the sun accurate to a fraction of a millimetre.
posted by Mike1024 at 12:34 AM on July 1, 2008
Well, sure, in the same way that integers are real numbers. I believe what hattifattener meant is: Computer operations on floating point numbers may produce results which do not match the results of manual arithmetic on real numbers. For example, (0.1 + 0.1) - 0.2 = 0 but (0.1 + 0.1 + 0.1) - 0.3 = 5.5511e-017; (2^100+2^10)-2^100 = 0, and so on.
Of course, you have to be dealing with pretty high precision numbers for floating point error to be a real concern; the 54 bits of a double-precision float can represent the distance from the earth to the sun accurate to a fraction of a millimetre.
posted by Mike1024 at 12:34 AM on July 1, 2008
Of course, you have to be dealing with pretty high precision numbers for floating point error to be a real concern
Not really. For example, catastrophic cancellation is one of the biggest pitfalls in the use of floating point, and there are others. For example, on my computer (Python on Linux):
>>> 3.14+1e28-1e28
0.0
Oops! And it doesn't go away when you use decimal floating point either:
>>> from decimal import Decimal
>>> Decimal("3.14") + Decimal("1e28") - Decimal("1e28")
Decimal("0E+1")
Oops!
You really need to identify why binary floating point is not working for you to find a solution. What is your application?
Decimal floating point is not a silver bullet, and will suffer from some of the same problems as binary floating point (plus it will be slow). If you don't know what these problems are, and to be honest, it doesn't seem like you do, you will have errors creep into your code, and answers that are wrong. Even if you do know what you're doing, it's pretty easy to introduce errors and numerical instability into calculations, so beware.
posted by grouse at 1:35 AM on July 1, 2008
Not really. For example, catastrophic cancellation is one of the biggest pitfalls in the use of floating point, and there are others. For example, on my computer (Python on Linux):
>>> 3.14+1e28-1e28
0.0
Oops! And it doesn't go away when you use decimal floating point either:
>>> from decimal import Decimal
>>> Decimal("3.14") + Decimal("1e28") - Decimal("1e28")
Decimal("0E+1")
Oops!
You really need to identify why binary floating point is not working for you to find a solution. What is your application?
Decimal floating point is not a silver bullet, and will suffer from some of the same problems as binary floating point (plus it will be slow). If you don't know what these problems are, and to be honest, it doesn't seem like you do, you will have errors creep into your code, and answers that are wrong. Even if you do know what you're doing, it's pretty easy to introduce errors and numerical instability into calculations, so beware.
posted by grouse at 1:35 AM on July 1, 2008
Best answer: The original poster is aware of BigDecimal (a Ruby library) and I don't think it has the problems grouse mentions:
Looking around there's a Javascript port here that might help:
BigDecimal support for JavaScript
posted by gi_wrighty at 5:16 AM on July 1, 2008
>> BigDecimal("3.14") + BigDecimal("1e28") - BigDecimal("1e28")
=> #<BigDecimal:2aaaaef5b288,'0.314E1',18(63)>
Looking around there's a Javascript port here that might help:
BigDecimal support for JavaScript
posted by gi_wrighty at 5:16 AM on July 1, 2008
"Of course, you have to be dealing with pretty high precision numbers for floating point error to be a real concern"
Not really. For example, catastrophic cancellation is one of the biggest pitfalls in the use of floating point, and there are others.
Sorry; what I meant to say was: With the 54 bit precision of a standard double, rounding errors and catastrophic cancellation are only an issue with operations requiring precision around 15 orders of magnitude (or above).
(10000000000000000+1)-10000000000000000 =0
(1000000000000000+1)-1000000000000000 =1
Applications demanding this level of precision include financial mathematics, scientific calculations, calculators and spreadsheets.
In the past I have experienced faults initially attributed to floating point rounding which were in fact caused by programmer error. As such, before writing errors off as rounding it is wise to verify that this is the true cause!
posted by Mike1024 at 10:18 AM on July 1, 2008
Not really. For example, catastrophic cancellation is one of the biggest pitfalls in the use of floating point, and there are others.
Sorry; what I meant to say was: With the 54 bit precision of a standard double, rounding errors and catastrophic cancellation are only an issue with operations requiring precision around 15 orders of magnitude (or above).
(10000000000000000+1)-10000000000000000 =0
(1000000000000000+1)-1000000000000000 =1
Applications demanding this level of precision include financial mathematics, scientific calculations, calculators and spreadsheets.
In the past I have experienced faults initially attributed to floating point rounding which were in fact caused by programmer error. As such, before writing errors off as rounding it is wise to verify that this is the true cause!
posted by Mike1024 at 10:18 AM on July 1, 2008
This thread is closed to new comments.
posted by sergent at 8:27 PM on June 30, 2008