Hacker News new | past | comments | ask | show | jobs | submit login
0.30000000000000004 (google.com)
91 points by nixy on Oct 29, 2010 | hide | past | favorite | 128 comments



I have to question the value of CS educations when a post of this nature pops up every couple of weeks, as if it is news that floating point arithmetic as implemented today, is by its nature an approximation, and that this is more noticeable the less bits you have to play with, when working with values that can't be represented neatly.

Financial arithmetic? Convert to smallest unit and use integers or the currency data type du jour in your language, and don't act surprised when operations on 32-bit floating point don't yield the intuitively correct values.

If you understood the representation format, you'd understand why.


I don't think it's just that it's an approximation. We're used to doing math with approximations (0.33333...). What's unintuitive about it is that numbers with very simple finite expressions in decimal (0.3) can only be expressed by repeating binary floating points.

Even competent coders who usually work in higher-level languages can sometimes forget that the decimal representations they work with are actually approximations of binary numbers, which is responsible for other seemingly weird behavior (why does ~2 = -3?), and I doubt even most skilled computer scientists are used to converting decimals to arbitrary-precision binary in their head.

It is unintuitive, and it's not like it's hard to explain why. "You'd understand if you were smarter" is a cop out.


My point is, this is elementary stuff that you need to know as a computer scientist or self-taught hacker.

Whether you can convert a decimal number into its binary IEEE-754 equivalent, or not, is besides the point.

You just need to know that it is lossy for certain classes of decimal numbers, and how to avoid or mitigate those effects.


Sure, if you want to have computer hacking be a big part of your life you should know that numbers are binary on computers, and that that has side effects. But I don't think it betrays a serious lack of education that someone who does know that theoretically has never realized that practically means there's no such thing as 0.1 on a computer.


You theoretically shouldn't even have to be a computer scientist. A standard mathematical education ought to teach you that for any given base, the only numbers that can be represented in that base exactly are numbers where the prime factorization of the denominator contains only prime numbers that are also contained in the prime factorization of the base itself, or is simply 1 for integers. Base 10 has 2 and 5. .2 is 1/5, so it's precise. .5 is 1/2, so it's precise. 1/12 has 2 2s in it, which works, and a 3, which doesn't, so 1/12 is recurring.

But prime factorization seems to be taught for almost superstitious reasons, as some sort of math trick rather than a fundamental aspect of understanding numbers at even the most basic of levels, with neither students nor teachers nor the curriculum writers really understanding why this is in the lesson plan, so, yeah, I suppose you have to be some sort of super expert genius to swiftly realize that 1/3 can't be represented in a base-2 number. But it shouldn't be that way.


I think the connection that "computer numbers are base 2" is what's missing. You input them into the computer in base 10, and they are printed in base 10. The base 10 representation is wrong because information was lost when converting to a fixed-width base 2 number. But that is hidden by the compiler or program, so it confuses programmers and users.

It works in Excel...


Excel does "cosmetic" rounding, that ends up producing some surprising results - in the words of this author, it "confers supernatural powers upon some (not all) parentheses": http://www.cs.berkeley.edu/~wkahan/Mindless.pdf


0.1 is perfectly representable "on a computer", just not in binary floating point.


See e.g. Haskell's Data.Ratio (http://haskell.org/ghc/docs/6.12.1/html/libraries/base-4.2.0...) module for rational numbers.


Or computers with decimal floating point hardware.


Yes, but that's such a special case. I'd rather go with base 11 than decimal.


Well, if you take Java for example, floating point measurements are only IEEE-754 when classes or methods are declared with the strictfp keyword, yet non-strictfp floating point calculations are still imprecise because of the very nature of fractional math. They're just less imprecise by default. I agree with the sentiment others have posted that it is disappointing that this reality isn't more commonly taught in CS education.


Things get a lot easier when you're working with tools that help you out, even just a little.

Clojure here, FWIW:

(+ 0.1M 0.2M) => 0.3M

That 'M' suffix denotes a BigDecimal, which provides for arbitrary-precision decimal math (which Clojure's arithmetic ops dispatch to as necessary).

A similar 'N' suffix is coming in the next release that denotes (contagious) BigInteger math (though as fast as longs when values are < 2^31), so one doesn't have to worry about overflow issues (which rate up there with misunderstandings of internal floating point representations in terms of frequency).

Other reader syntax is provided for other common notations (e.g. 10e6, 16rFF, 0xFF, 0220, 2r100010101).


Just be careful to not use BigDecimal when you really should be using an int (e.g. currency).


Why would you use an int for currency? Isn't that what BigDecimal is for?


Historically finance is done with integer multiples of some fraction of the currency. This allows for easy balancing of both sides of any transaction without things getting too out-of-hand. It's common to see $0.001 as the minimal unit in the US, for example.

If you used a decimal fractional representation instead, then one transaction of higher precision would contaminate all accounts it touches, making printing exact balances a pain.


Talk me through this. As I understood it, and I must be wrong here, a decimal type is an abstraction around an infinite-precision integer, which represents something like thousandths of a cent? I thought there was an IEEE standard, and it was the standard for all financial transactions. Is this not what BigDecimal is?


So, we lop off some precision and call it correct because displaying extra decimal places would be a "pain?" Not sure I'm following your logic here; if my balance is $10.0000005, wouldn't it be wrong to change that to $10.000?

If you want to do this correctly, then there should never be a balance that has an amount under the minimal unit. Then it doesn't matter if you use a BigDecimal or an int.


This seems like the right way to do it.

Though, I assume non-suffixed literals are still regular floats?


Yes; insofar as maximal perf is a desirable default characteristic, `0.2` must continue to be a regular decimal value (technically, a double in Clojure). We've already been down the path of having to extensively hint Clojure code to get the best perf, and we'll likely not revisit that.

That said, the above notation makes advice to those who are having trouble a lot easier.


> This seems like the right way to do it.

It's also slow as molasses, which is why very few languages default to decimal floats and use IEEE floats (or doubles) instead, via their hardware implementation. The behavior of IEEE floats and doubles is very well defined, and though they are unfit in some sectors (you do not want to count money using them), have to be massaged a bit when displayed and don't deal well with great differences in powers-of-10 (e.g. 1e21 + 1 == 1e21) they work well enough in practice. And they're implemented in hardware.


I remember reading that Java has one difference from IEEE754 that might bite somebody once in a while: floating point modulo. But I can't find a reference

Seems like other delta's (e.g. how many NaN's are implemented) don't much matter.

http://java.sun.com/docs/books/jvms/second_edition/html/Conc...


I'm aware of the performance hit, I just like the approach of a BigDecimal literal instead of having to write that boilerplate.


> I just like the approach of a BigDecimal literal instead of having to write that boilerplate.

Ah yes. Well not all languages require a bunch of boilerplate either. In Python for instance, there is a type decimal.Decimal which you can just alias to `d` and write:

    val = d('0.1')


I do wish there were a syntax for Decimal literals, e.g. 0.1D (analogous to 100L as long, etc.). Maybe I'll make it my weekend project even if I don't really expect it to be accepted into CPython proper.


I'm pretty sure the L syntax has gone away in Python3, with numbers being auto-promoted to Long if they're going to overflow. I'm a bit hazy about this, but I think I talked to the maintainer over lunch at PyCon two years ago.


There's actually no externally-visible long type at all in Python 3, it's all done transparently now. That said, I'm not using Python 3. The promoting behavior you speak of is how Python 2 works.


Indeed it has, integers and longs have been unified as a single `int` type, so the external marker is gone as well.


Erlang likewise:

1> 0.1 + 0.3.

0.4


Nope, Erlang's floats are still IEEE754 floats:

    1> 1.0e21 + 1 == 1.0e21.
    true


Right. It has arbitrary-precision ints, but lossy floats.


> We're used to doing math with approximations (0.33333…)

0.33333… is not an approximation.


True enough. I was trying to imply the use of 0.33333 as standing in for 1/3, rather than exactly 0.333330..., but of course 0.3... is an exact representation of 1/3.


You mean 3/10.


I actually mean 0.999.../2.999... .


That's just plain irrational.


No. If it repeats, it's rational.


I would't necessarily call this a failing of CS education specifically - some, or even most of these questions may have come from self-taught individuals.

What does worry me is how many times this question is asked on The Web. This is a sad indictment on the quality of education in general. Given this resource, where information is far easier to find than in any library, there are still so many people who can't be bothered to look things up for themselves.


Well, some of us (before uqiquitous home computers) actually pulled out the little manual that came with our calculators to learn about "binary coded decimal", and some didn't.

OK, I'm probably giving away too much about my age :-)

And some had better things to do anyway...

That said, yes, this does get misprogrammed in financial applications very frequently. FWIW, I wrote a little tuturial about this very issue a while back: http://roboprogs.com/devel/2010.02.html


It would be nice if more languages supported fractional rational number arithmetic as a built-in type. Converting into fractional units and back again shouldn't be as much of a pain as it is in most languages at the moment.


One of the things I love about Scheme is not having to worry about number formats. It's a number, it is what it is. You never run out of digits, you never have to worry about precision errors. It removed a layer of stress I never knew existed.


Actually you are talking about a specific implementation of Scheme. Probably PLT Scheme? An standard compliant implementation can just stick to floating point for everything as the bare miminum. PLT Scheme is the Scheme to go for, though.


Any fully-compliant R5RS really. I'm talking about the concepts of exactness and the numerical tower in particular. The notion that an integer is a subset of a real number instead of a whole separate type is conceptually very nice.

http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z...


Yes, and: "Implementations of Scheme are not required to implement the whole tower of subtypes given in section 6.2.1, but they must implement a coherent subset consistent with both the purposes of the implementation and the spirit of the Scheme language. For example, an implementation in which all numbers are real may still be quite useful." (From your URL)


True, but I think you're picking at hairs here.

The suggestion in the R5RS spec is that you shouldn't need to worry about the segregation of integer and real types (unless you need to). And indeed, why should you?

It's a conceptual shift away from char, int, long, float and double, which exist only because they're easier for the computer to deal with. Scheme was the first language I used that attempted to treat numbers like numbers.

Besides, Exactness is required in R5RS and the full numerical stack is no longer optional in R6RS.


Yes, even Guile seems to have acquired a proper tower nowadays.


It is easy in Ruby and Python.


There are a LOT of programmers who don't understand how the fundamentals are implemented, though. I'm not saying you need to use C (or machine language) for everything, but having an idea how the whole stack fits together will show which operations are inherently more expensive, error prone, etc.

Two books I'd recommend: _Write Great Code vol. 1: Understanding the Machine_ by Randall Hyde, and _The Elements of Computing Systems_ by Nisan & Schocken (http://www.idc.ac.il/tecs/). WGC1 is about things typically learned about the machine while learning assembly (but leaves assembly itself for later). TECS guides you through building a (virtual) computer, starting with simulated NAND gates and building up. (Admittedly, I just got that one and I'm only a few chapters in, but excellent so far.)

I think every developer would benefit from knowing at least one low-level language (such as C), and one very high level language (such as Prolog).


I'm also a few chapters into The Elements of Computing Systems, and I also think it's excellent so far :)

I'm posting my project solutions to my github account, fwiw: http://github.com/happy4crazy/elements_of_computing_systems


I have to question the value of CS educations

Many people have no interest in being computer scientists. They want to learn enough software engineering to get something done using a computer. Outside of CS, programming is only a means to an end.

Higher level languages ought not to expose the user/programmer to this sort of thing, even at the expense of performance; for every 'why is it that...' question, there are probably several more buggy programs being used in production environments. Also, it's a confusing distraction for kids trying to learn programming, who may not be able to wrap their heads around the theory of FP implementation, although they may be quite competent to handle the task they're trying to implement in software.

I used to work with a Motorola 56k series DSP, so named because it offers up to 56 bits of integer precision. Dealing with data that's usually represented as gloating point involves a normalization conversion as you describe for the currency unit which soon becomes second nature. But for newcomers to the platform, that step at first seems like an inefficient limitation, adding complexity to an otherwise simple algorithm. Not everybody wants or needs to know how the underlying hardware works, often they just want to implement some fairly trivial math or logic without worrying about accuracy.

I can't bring myself to correct that spelling error :)


To me its only an indicator.

There's a ton of stuff on similar level that does matter and it adds up.

Phenomena like knowing O(n) of basic algorithms and data structures. Understanding basic file formats and how they are implemented. Basic knowledge of network protocols and their building blocks. Basic knowledge of assembler. Understanding how IO happens and how your use case is bound (IO or CPU). Knowledge of character code pages, etc.

Nothing of the above is necessary to program in high level language. But if you want to do it well they can't possibly harm you. Or you will get involved with a vendor product that was written by someone who didn't know the above and you will be forced to reverse engineer the goddamn thing just to get your job done. :)


I totally agree where full-time development is concerned. And it's the sort of thing macro languages and so on tend to build in.

There are lot of people that might need to do a bit more programming - more elaborate than what a spreadsheet easily allows, but which doesn't justify becoming/hiring a pro. I was surprised to find last year that there are programming languages designed specifically for knitting, for example.


"I have to question the value of CS educations when a post of this nature pops up every couple of weeks..."

How do we know it's not freshmen, high school students, and self taught developers asking the question?

From what is currently the top hit for that search:

"I am writing a program for class project..."


I bet many many people who "do a bit of programming" have no formal CS training, and only begin to understand the complexities of floating point arithmetic when they come across a problem like this (and hence ask the question)

Oh, this applies to me btw.


And they only really remember the issue after it bites them in the ass. Most CS majors, for that matter, probably don't really think about or remember the issue until after it bites them. I would expect a really good programming course (if one exists), to present exercises that actively cause the students to run into these types of problems, but from what I have seen there has been a progressive de-emphasis on "trick" questions and really confusing problems, at least in some schools, in favor of self-esteem and "empowerment".


Look at the answers people give in the search. It doesn't help that every time someone asks this they get a link to something like this: http://docs.sun.com/source/806-3568/ncg_goldberg.html

I'm surprised someone hasn't translated it into Klingon or Sanskrit yet.

It's pure snobbery that makes people leave responses like this. This document falls into the further reading for enthusiasts category. Nobody should be linking to it as an introductory answer to the question. At least not if they want people to learn and stop asking the question eventually.


> Financial arithmetic? Convert to smallest unit and use integers or the currency data type du jour in your language, and don't act surprised when operations on 32-bit floating point don't yield the intuitively correct values.

Yes, though you may want more precision then that. For accuracy you could use, say, exact rational numbers, and for speed a fixed precision below e.g. Cents might be useful. (Though you probably meant something like nano-Cent when you said, convert to the smallest unit.)


Not so much that this is a question, but that this particular question gets 100+ points on "hacker news", when its not even a question or explanation, just a link to google result of adding numbers in java.

If it was at least a homework question on stackoverflow there would at least be a good description, and possibly a reasonable explanation.


Apple's Calculator app used to have an odd floating point rounding behavior (I filed a bug report and they fixed it):

    14479.14 
    - 
    152.36 
    = 
    (result is 14326.78) 
    1143 
    / 
    78 
    = 
    (result is 14.6538461538461) 
    14479.14 
    - 
    152.36 
    = 
    (result is 14326.7799999999884)
Note that the first and third calculations are the same, yet they resulted in different displayed results!

I never understood this bug. I understand floating point, so understand that some numbers are not exactly representable. However, it should at least still be consistent! The same calculation should give the same results every time.


I suppose it depends how exact you are in defining 'same calculation' -- two possible causes of inconsistency are:

* FP means the rules of algebra do not hold, so if the calculation is done in a different equivalent form, you can get a different result.

* With Intel's old FPU unit, if a value is written to memory rather than staying in registers you can get a different result.


Welcome to the best lectures of your life:

http://webcast.berkeley.edu/course_details_new.php?seriesid=...

also available via iTunes U. I'm currently listening to them on my commute. Note, if you do actually go through the whole course -- you'll need to listen to a different year for lecture 24 or so -- that one is skipped. One of the highlights of my day is actually coming home and re-looking up what he's talking about.

(Oh, and of course you can just listen to the two Floating Point lectures. It has to do with the non-uniform -- or at least non-linearly uniform mapping of numbers, represented with a significand/mantissa and an exponent, onto the set of real numbers + the fact that the exponent used is in base 2, in the hardware, so the floating point numbers are spread about in a particular way. The difference between real numbers, as you tick up the odometer with each bit, varies depending on where you are in the number line (with big numbers, it's actually much more, with smaller numbers it's pretty minimal, but not unnoticeable as seen with this example. Does that make sense? Maybe I'm off about this... Anyway, still obviously recommend the lectures. And now, I'm going to read up more on ALUs and MUXs..)


http://0.30000000000000004.com/

Because why not? I've populated it with some languages that I can convenient access to an interpreter for. If you post/send me .1 + .2 in any other languages, I'll try and put them up.


GHC (Haskell):

  $ ghci
  GHCi, version 6.12.1: http://www.haskell.org/ghc/  :? for help
  0.1 Loading package ghc-prim ... linking ... done.
  Loading package integer-gmp ... linking ... done.
  Loading package base ... linking ... done.
  Prelude> 0.1 + 0.2
  0.30000000000000004
And just for fun with GHC's rational numbers:

  Prelude> :m + Data.Ratio
  Prelude Data.Ratio> (1 % 10) + (2 % 10)
  3 % 10
Hugs (Haskell):

  $ hugs
  Hugs> 0.1 + 0.2
  0.3
bc:

  $ bc
  0.1 + 0.2
  .3
Gforth:

  $ gforth 
  0.1e 0.2e f+ f. 0.3  ok
dc:

  $ dc
  0.1 0.2 + p
  .3


Thanks, I've added them. Not only do I not have an interpreter for any of these languages handy, I've never even used them!


You're welcome.

bc and dc are probably installed on your Linux or Unix box.

By the way, you should fix "Below are some examples of sending .1 + .2 to standard output in a variety of common languages." to "[...] in a variety of languages."

It also seems like the names of bc and dc are non-capitalized.

For the Haskell entry, please just shorten it to "0.1 + 0.2". The "Prelude>" thing is just a prompt for the REPL. ":m + Data.Ratio" loads the rational number module, please take the entry about Haskell's rational numbers out since yours is a page about floating point. (You might want to replace it with a comment, that Haskell supports rational numbers. But so do lots of languages in their libraries.)

C would be a good addition. (Plus Fortran, Cobol, Ada and J.)


Fixed. I've moved the rational number stuff to a comment, I'm working on the formatting, but I think what I've got now works.


Good. By the way, having OR in the middle column and AND in the right column seems a bit strange.


Racket (aka PLT Scheme):

  $ racket
  Welcome to Racket v5.0.1.
  > (+ .1 .2)
  0.30000000000000004
  > (+ 1/10 2/10)
  3/10
Steel Bank Common Lisp:

  $ sbcl
  This is SBCL 1.0.43 [...elided]
  * (+ .1 .2)
  
  0.3
  * (+ 1/10 2/10)
  
  3/10


Thanks, added.


Go (via the online system at http://golang.org/)

  package main
  import "fmt"
  func main() {
    fmt.Println(.1+.2)
  }
0.3


thanks, added


IIRC, Ruby supports rational numbers through http://ruby-doc.org/core/classes/Rational.html


Powershell:

    PS C:\> 0.1 + 0.2
    0.3


This is the type of thing that makes mathematicians flying tackle computer scientists.

the CS: "Hey, it's round off error. Get used to it"

the Mathematician: "Fix IT!!"


It's not "round off error", though. More like:

"It's a base 2 fractional number with no exact decimal expansion with a finite number of digits. Display it in base 2 fractional form if you don't want to see an approximation."


Well, it's more like:

Binary numbers are a countably infinite set. Decimal numbers are a countably infinite set. You can therefore map binary representation to decimal representation 1-1 with a simple mapping function.

OTOH, floating point numbers are an uncountably infinite set. The only way to map binary numbers to floating point numbers is to map to a strict subset.

The subset happens to be different when mapping binary to floating point and decimal to floating point when using IEEE754.


The mathematician: That's fine for 22/7. Now, go fix your computer so it knows how to count to 0.3, kthxbye


As a mathematician I do not value 0.3 above 22/7.


0.3 is a rational too, it's just 3/10.

Anyway, mathematicians don't use numbers at all - debasing the equations by performing caculations with them is so.. gauche. Leave that to the physicists, chemists and engineers.


I saw the occasional number in my studies.

We'd rather calculate with knows than numbers. (See http://en.wikipedia.org/wiki/Knot_theory)


No doubt, for years I've called quantization errors round off errors. But there's no rounding, it's an approximation error.


> This is the type of thing that makes mathematicians flying tackle computer scientists.

Well, it's fixable. You lose your deal of performance in order to get some absolutely valid rational numbers representation.

It's only that most tasks don't require this kind of precision.


And then you discover that you have a square root in your computation.


Even then it doesn't matter fully. There are some languages with supported data types where you can actually write the equivalent of.

  x = 2
  y = 2
  1.upto 100 do
    x = x ** (1 / 2)
  end
  1.upto 100 do
    x = x ** 2 
  end
  x == y # => true
because it handles the math by only evaluating the operations at the very end.


Which ones? It honestly seems unlikely to me that this would work well in practice.


Good point, but rational numbers aren't closed under power/square root operation :)


Well, which data type would you like to store the value of PI in? I mean, you could re-calculate it to arbitrary precision every time you use it... just don't try to store it at "full" precision using floats or decimals.


If it where me I'd store the value of PI as a structure containing an IEEE double, a decimal representation of, say, 100 digits and a pointer to a function to that can evaluate PI to an arbitrary precision.

Then I'd just just use PI as a symbol and do symbolic algebra for as long as possible. Don't evaluate it to anything until the user actually asks for a numeric representation. Once a numeric representation is required I'd pick a value from the above structure based on what has been asked for.




From Slashdot: http://developers.slashdot.org/story/10/05/02/1427214/What-E...

Every programmer forum gets a steady stream of novice questions about numbers not 'adding up.'...


Leaving ego's aside, while the reasons for this are obvious to anyone who learned programming with binary and old school stuff like that, there is a whole genre (class) of programmer that has learned what is needed to build web sites and unless you have large scale issues (which you solve by hiring someone with old school CS skills) you can happily be competent and successful without every knowing about how languages implement floating point math. I think it is wrong to belittle these people because I have worked with them and sometimes, I have found, it is their skills that are more often more influential in the success of a product that the CS guy in the back room tweaking the slab allocator. Times have changed. It's no longer crucial for every one who deserves the title 'developer' to know about these kinds of language nuances.


Brendan Eich (Creator of JS) discussed this here: http://www.aminutewithbrendan.com/pages/20101025 as it pertains to JS, but it is similar for other languages that implement IEEE double precision numbers


I think ECMA script uses double-precision 64-bit binary format IEEE 754 values for all number storage, or are there browser differences I don't know about which cause problems?


People often say "Just use (x) decimal arithmetic system for important stuff like finances."

Out of curiosity, I'm wondering how much trade you would have to be doing for floating-point imprecision to cause an actual problem.

Taking 0.2+0.1 as an example and figuring an imprecision of $0.00000000000000004 per $0.30, figuring a loss of one cent as being significant, I have 0.01/(0.00000000000000004 / 0.3) = 7.5e13, or... seventy-five trillion dollars?

Never mind that you're as likely to get 0.6+0.1 = 0.69999999999999996, which should roughly cancel out the error over time.

This is basically just an aesthetic problem in finance, yes?


Nope, it makes code more complex. For example, if the customer makes payments in 2 installs and you want to check if they have payed the full amount, you can't do "if (a + b >= c)" if you use floating point. You'd have to use something like "if (a + b >= c - 0.01)".

A normal double (IEEE 64) has only ~15 digits of precision, so when the amounts grow large, you loose the precision for the cents.

Example (in Java, which uses IEEE): double d = 1e9; System.err.println("d: " + d); d += 0.01; System.err.println("d: " + d); d -= 1e9; System.err.println("d: " + d); d -= 0.01; System.err.println("d: " + d);

What is 'd' at the end? Not zero. This adds a gazilion weird cases in the code that you have to handle.

Some countries have laws are very strict about how rounding should be performed and that all amounts must be an integral number of 'cents', so using doubles are completely out of the question in those cases.


Ah, completely true.


How else but for imprecision would you channel the half cents to rip off your soul deadening job?


There is a term for this in mathematics and now I can't remember it, but with some systems, e.g. number crunching with large matrices, a very small change like this can magnify into huge differences and give you totally wrong results. It's definitely something you must be aware of and take into account.


Conditional stability?


The other related term is `stiffness'.


That's it.


Yeah, I guess if you're doing more complex math with the figures than just addition and subtraction there's the possibility for magnification effects. I'd be interested to see an actual example from business, though.


Don't know about business, but when you're working with audio it's very common to multiply the contents of a stream against those of an array, such as a filter kernel with the coefficients computed in advance. With multiple channels which are being mixed in unpredictable ways, there's plenty of room for errors to propagate.

Most of the time the only result is an imperceptible rise in noise, but it's not uncommon to have threshold-dependent routing of signal flow or for audio signals to used as input to modulation processors and vice versa. Every audio synthesis tool I've ever used had at least one bug resulting from error accumulation. They don't stand out very well in testing, but then people start reporting things like I was playing a tune with this patch and stopped to eat dinner, but when I came back my keyboard would only make a horrible noise, is it broken?


> which should roughly cancel out the error over time.

To be pedantic, if the errors of size e are as likely to go one way as the other for each of N steps, the expected magnitude of the total error will be sqrt(N)e. It's a random walk.


Another example would be conversion dimensions back and forth between imperial and metric. If you're using floating point carelessly, it can introduce enough distortion to derived calculations (say, volume or material cost per cubic meter) to cause pricing or manufacturing errors.


If you really want precision and you're dealing only with rational numbers, it's better to maintain a struct rational { u64 numerator, denominator; }; and do all calculations with it.


Funny thing. Google has this page at rank 5 by now (after 42 mins).


Hacker News has intrinsic value


I remember the discussion on floating point and talking about BCD (we had assembler on and IBM/370), but I get the feeling BCDs are not talked about much anymore given some of the discussions I've had over the years. A related thing to watch out for is any arithmetic with units and how to deal with fractional conversions. This could cost you a lot of money or mess up and inventory if handled poorly.


People who see this fall into one of two kinds: those who are shocked by this, and those who are shocked by those who are shocked by this.


Actually there's a third category of us who understand why it happens and understand why others don't understand.


I just tried this in javascript (via Firebug) out of curiosity and it's the same problem.

0.1 + 0.2 = 0.30000000000000004


> and it's the same problem.

it's not a problem.


It's not a bug, it's a feature or better said it's a known issue which should be taken into account when writing a program that uses floating point numbers.


I consider it a bug in the specification that all numbers are floating-point in javascript.


I'd like to submit a bug: when I stare directly at the sun, my eyes begin to hurt. Also, there's this weird force pulling me towards the Earth, what's that all about?


http://docs.sun.com/source/806-3568/ncg_goldberg.html

However, my problem is that modern language are hiding these things for you.


Is this IEEE-754 specific?

If so, are there any IEEE-754 alternatives?


Yes it is IEEE-754 specific. I think any binary floating point will have similar pitfalls though.

Python has a Decimal type that can represent decimal values exactly. It must be imported first though. It is probably much slower than IEEE-754 floating point, but for many uses that is not an issue. http://docs.python.org/library/decimal.html


The alternative is a revision of IEEE 754 to include "decimal floating-Point arithmetic," which is apparently something "in progress" because the interests of those involved can't result in agreement. I'd really like to read anything from some insider. I know that IBM did the most of the work in that direction. Decimal Floating Point is the thing to have, but who know when that can happen in processors or in wider use. I know from Brendan Eich that there were attempts to add DFP in Javascript but that even there no agreement was achieved.

Edit: or it was standardized but still not gaining acceptance:

http://en.wikipedia.org/wiki/IEEE_754-2008


Yes, that's http://en.wikipedia.org/wiki/IEEE_754-1985 specific. I like this explanation: http://docs.python.org/tutorial/floatingpoint.html#represent...

There is alternatives but don't forget that it is hardware issue not software. It looks like http://en.wikipedia.org/wiki/IEEE_754-2008 has decimal format thus it should address that.


It is both specific and non-specific depending on what you are trying to achieve.

In the special case of 0.1 + 0.2 != 0.3, this is specific, and using decimal will "fix" it. But decimal, or for that matter any representation will have the exact same issue for other cases. Arbitrary precision means exactly that: arbitrary, as in not infinite precision. How will you represent irrational numbers with decimal ? It is theoretically impossible.

That's why most advices about using decimals instead of IEEE 754 are not appropriate. In the special case of accounting, it is appropriate, but I am somewhat doubtful most people don't need to do occasional computation with their numbers involving transcendental functions (e.g. log, exp, etc...). As soon as you start using this, you will see the issue cropping up, whatever representation you may want to use. And IEEE 754 representation has been conceived by people who really knew what they were doing (e.g. W. Kahan)


> If so, are there any IEEE-754 alternatives?

Decimal arithmetics, which incurs a severe performance loss as IEE754 floats and doubles are implemented in hardware.


In any practical sense, no. The inconvenience is overwhelmingly offset by it working the same everywhere. And as others have said, you can sacrifice speed for it working "properly" anytime.


irb(main):002:0> 0.1 + 0.2 => 0.30000000000000004


I want a QPU unit!


If only IEEE 754-2008 would gain traction...




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: