24 ms·
Why does 0.1 and 0.2 = 0.30000000000000004?
- whitten 4y agoThis result is strictly a result of floating point math. You never get this kind of result with fixed point or binary coded decimal math. The computer Language M never has this kind of problem.
- compiler-guy 4y agoThe article isn’t about the imprecision in general, but rather about the details of this particular equations imprecision, and how that leads to this particular answer.
- pwdisswordfishc 4y agoHow do you represent 1/10 exactly in binary fixed-point representation?
- tsukikage 4y ago...or, indeed, 1/3 as binary coded decimal.
- amelius 4y ago> How do you represent 1/10 exactly in binary fixed-point representation? The base of your number system does not need to be the same as the base associated with the fixed point position. Easiest to explain: if you have a uint64 that represents a monetary value, you can let it express the number of dollarcents instead of dollars. Then you can express 1/10 dollar as 10 dollarcents.
- jstanley 4y agoThe claim was "you never get this kind of result with fixed point math", not "you can contrive to avoid this kind of result with fixed point math if you use a different base for the fractional part".
- whitten 4y ago1/10 is exactly 0.1
- lifthrasiir 4y agoSo 1/3 + 1/3 + 1/3 = 1 in M, right?
- dragonwriter 4y agoWell, it is in racket? Welcome to Racket v8.7 [cs]. > (/ 1 3) 1/3 > (+ (/ 1 3) (/ 1 3) (/ 1 3)) 1 > (integer? (+ (/ 1 3) (/ 1 3) (/ 1 3))) #t
- PaulHoule 4y agoIt is not "floating point math" but "floating point math where the exponent is expressed as a power of 2" that is the problem. That is, if the exponent is a power of 2 you can write 1/2, 1/4, 1/8 exactly but you can't write 1/3, 1/5, 1/10, etc. If the exponent is base 10 then you can write 1/5, 1/10, 1/1000 and such exactly. Note the mantissa and the exponent are both integers and so far as this problem is concerned it does not matter if these are written in binary or BCD or some other representation. There is some controversy about what is better, if you use a binary mantissa the math is a little faster and more accurate, if you use a decimal mantissa conversions to and from ASCII are quicker and ASCII conversions are a major part of real life math workloads. I've long thought that this problem is one of a list of problems that many people encounter on the path to bending computers to their will and that some people decide that computer programming isn't for them because of this kind of problem. I think the kind of person who learns Python to put their outside-of-computing skills on wheels is particularly affected. It is a "disruptive technology" problem because the person who is using IEEE floats heavily has accommodated to this problem and would not give up the slightest amount of performance. Decimal FP can be implemented in software but is slow in software. IBM has had hardware Decimal FP in their mainframes for a very long time and there is even an IEEE standard. (A company that has been using mainframes for a long time cut a check for the wrong amount because the abused the number system back in 1963 and thus learned their lesson a long time ago.) The best hope I have is that the "social justice" people can be led to believe that unintuitive numerics keep underrepresented people out of the field and that they threaten Intel that they'll tear down their headquarters unless they catch up to where mainframes were 50 years ago. It could be a huge win for the industry and for the DEI office because employers would have to buy everyone a new computer and even white guys might think the DEI office was doing good work if it meant they got to replace their 5 year old corporate craptop. I mean, how is it that a few people with two fingers get to oppress all the rest of us with ten?
- edflsafoiewq 4y agoFixed point behaves well under addition and multiplication by integers. Otherwise it isn't very good. There are far more numbers in the range (1,infinity) than (0,1) for example, so 1/x loses catastrophic amounts of information.
- dragonwriter 4y ago> This result is strictly a result of floating point math. This specific result is a result of binary floating point math with a particular precision. More precision, or decimal floating point, will fix that, but have similar kinds of errors for the same operation on different numbers. fixed-size BCD/fixed-point math has other limitations, its not a general solution. The general solution is to have a numeric tower where representations and operations meet the following rules: 1. The default representation of any exact literal (without a modifier representing a particular inexact representation, e.g., as an optimization or a necessity of interfacing with an external library) is exact, 2. Any operation on any operations between exact representations that can be done exactly is unless specified otherwise (as it might be for the same reasons discussed above), and stored in a representation that can represent the result exactly. 3. Essentially inexact operations or operations on inexact numbers are conducted in a way and produce output representations that minimize additional imprecision introduced, except when explicitly specified otherwise. Computer algebra systems where the “top level representation is symbolic are potentially the ultimate expression of this, but the Scheme numeric tower is pretty good (but at least Racket, and I think schemes in general, represent exact decimal fractions as floats still, so don’t entirely avoid the problem, but at least division of integers produces exact rationals.) Lots of languages default to putting numbers expressed as literals into either fixed-sized integers (not bad, especially when those are often 64-bit now which rarely has much practical distinction from arbitrary precision in most applications) or fixed-sized binary floats (which are more problematic, especially given the mismatch between clean binary and clean decimal representations.) This is very good for efficiency, because computers can process fixed sized integers and binary floats very quickly. But, especially for floats, it can be bad for correctness when doing arithmetic where the input is all clean decimal literals.
- aag 4y agoYes, Scheme has the concept of exactness[0]. In most Schemes, (exact? 2.3) ==> #f. But at least the exact? procedure exists. It can tell you whether your result might have been affected by these problems. If you want perfect answers, you can always use the rationals that are built into Scheme implementations with the full numeric tower. [0] https://standards.scheme.org/corrected-r7rs/r7rs-Z-H-8.html#TAG:__tex2page_sec_6.2.2 https://standards.scheme.org/corrected-r7rs/r7rs-Z-H-8.html#...
- dragonwriter 4y agoTangentially (and separate from my other response because it's a whole different issue): > The computer Language M Which one? MUMPS (also known as M), or the Power Query Formula Language (also known as M)? OR something else?
- whitten 4y agoM alternately named MUMPS M guarantees over 15 digits of precision, which is why it is heavily used in banking and financial applications
- twawaaay 4y agoThe answer is: "If it matters to you, you should not be doing FP math."
- stefncb 4y agoLooks like the title got messed up in the process; the + sign was definitely not supposed to be 'and'.
- compiler-guy 4y agoPeople often use “and” to mean addition. “One and one make two”. So I think the title works fine.
- stefncb 4y agoI personally read it as 0.1 and 0.2 are both equal to 0.3000-whatever. Maybe it's because English is my second language.
- js2 4y ago"and" as a conjunction is sometimes used to mean "plus". As in "two and two make four."
- asicsp 4y agoSee also: Floating Point visually explained (https://fabiensanglard.net/floating_point_visually_explained/ https://fabiensanglard.net/floating_point_visually_explained...) — for those allergic to mathematic notations There's also a stackoverflow thread: https://stackoverflow.com/q/588004 https://stackoverflow.com/q/588004
- brazzy 4y agoAlso https://floating-point-gui.de/ https://floating-point-gui.de/
- johnklos 4y agoI love when someone figures out the detailed reasoning behind why some things are the way they are. Sometimes I go down those rabbit holes myself and never figure it out, but when I do, it feels so good! Just as reading articles like this. Thanks, Julia!
- tiffanyh 4y agoTL;DR; 0.1 in binary, is a repeating decimal - just like how 1/3 in base 10 is a repeating decimal. So you get errors due to rounding. This is also why it's super important in loops to never use "=" as a condition statement, but instead use "<=" (or ">="). Otherwise you might create an infinite loop.
- MuffinFlavored 4y agohttps://docs.rs/rust_decimal/latest/rust_decimal/ https://docs.rs/rust_decimal/latest/rust_decimal/ Or something like this?
- jacobsenscott 4y agoThe choice of decimal vs float depends on domain. For financial calculations you want to be able to represent amounts exactly. Decimal can represent $1.01 exactly, but float cannot (it picks a value very close to that, and as you do calculations eventually these small inaccuracies amount to real money). But if you are storing the weight of a product a float might be fine - you don't really care if the system thinks you have 50.000001 pounds of product when you have 50 pounds (because your scale isn't that accurate anyway). Floats will generally give you better performance than decimal types as well.
- MuffinFlavored 4y agoWould you say it's "odd" that popular languages (C#, Java, JavaScript/node.js/TypeScript, Python) don't have built in decimal libraries?
- MichaelNolan 4y agoJava has BigDecimal. Python has a decimal and a fraction class. I’m assuming C# has something apart of their standard library as well. And JS/TS has third party libraries that can for arbitrary length precision math as well.
- deleted 4y ago[deleted]
- bricss 4y agoTL;DR: Bocs of IEEE 754 encoding, yo
- deleted 4y ago[deleted]
- adunk 4y agoI really like this quote from the article as way of explaining this whole perceived anomaly: > To me, 0.1000000000000000055511151231257827021181583404541015625 + 0.200000000000000011102230246251565404236316680908203125 = 0.3000000000000000444089209850062616169452667236328125 feels less surprising than 0.1 + 0.2 = 0.30000000000000004.
- lifthrasiir 4y agoAnd in my opinion, this is why `0.1 + 0.2 = 0.30000000000000004` is a bad meme. It cements a very wrong perception about floating point numbers. If we denote a rounding operation as `f64(...)`, this is `f64(f64(0.1) + f64(0.2)) = f64(0.30000000000000004) != f64(0.1 + 0.2)` which can obviously happen. In particular, `f64(0.1) != 0.1` etc. but we happened to choose 0.1 as a representative for `f64(0.1)` for various reasons. Nothing inaccurate, nothing meme-worthy, just implied operations.
- crdrost 4y agoYes, this is the best way to explain it. Your number literals are “snapping to a grid” that is not base-10 and then we choose the shortest base-10 decimal that snaps to the appropriate grid point when we stringify the number. The other thing that I would mention is that I see some really gnarly workarounds to try to get around this... Just bump up to integers for a second! People have this mistaken idea that the best way to understand these rounding “errors” is that floating point is just unpredictably noisy for everything, and that's not true. Floating point has an exact representation of all integers up to 2^53 – 1. If you are dealing with dollars and cents that clients are getting billed or whatever, okay, the best thing to do is to have a decimal library. But if you don't have a decimal library and it's just some in-game currency that you don't want to get these gnarly decimals on, 3/10 will always give 0.3. 4/100 will always give 0.04. Just use the fact that the integer arithmetic is exact: multiply by the base, round to nearest integer, do your math, and then divide out the base in the end: and you'll be good.
- ClumsyPilot 4y ago> If you are dealing with dollars and cents that clients are getting billed or whatever, okay, the best thing to do is to have a decimal library. C# has decimal in the base library. We are doing a new project with financial data, and decided we willvhave everything in decimal - no floats at all. There is no point of dealing with these issues to save irrelevant amount of CPU
- pletnes 4y agoI don’t agree with the title, but this article is great if you want to get to the very bottom of the floating point rabbit hole. https://people.cs.pitt.edu/~cho/cs1541/current/handouts/goldberg.pdf https://people.cs.pitt.edu/~cho/cs1541/current/handouts/gold... What Every Computer Scientist Should Know About Floating-Point Arithmetic One thing worth mentioning is that the IEEE 754 floating point standard is implemented in hardware in most CPUs (microcontrollers might deviate) so if you learn this stuff you’ll be set for life, as in, it doesn’t depend on the programming language you’re using.
- abofh 4y agoVery large values of 0.1
- shagie 4y agohttps://0.30000000000000004.com https://0.30000000000000004.com is a fun site.
- dec0dedab0de 4y agoI've said this before, but I wish python and other high level languages defaulted to decimal for the literals, and made float something you had to do explicitly. My reasoning behind this is that floating point math is almost always an implementation detail, instead of what you're actually trying to do. Sure, decimal would be slower, but forcing people to use float as an optimization would remind them to mitigate the risks with rounding or whatever.
- lifthrasiir 4y agoThere is a very big catch---many if not most mathematical functions won't be exact anyway, so you have to round at some decimal places. Python does this with its `decimal` module: the number of fractional digits is literally a part of the global state [1]. While this allows for more concrete control over rounding, assuming that there was no such control, it turns out that the choice of radixes doesn't matter that much. [1] https://docs.python.org/3/library/decimal.html#context-objects https://docs.python.org/3/library/decimal.html#context-objec...
- scaredginger 4y agoWouldn't seriously suggest doing this, but rationals with big integers would have exact results for all the common operations
- runeks 4y agoI started out using the Haskell “Rational” type [1] (which is exactly what you mention) for https://cryptomarketdepth.com/ https://cryptomarketdepth.com/ but I had to abandon it because it was horribly slow. I was multiplying numbers with roughly 8 decimal places, and once I had done this like 100 times my program spent almost all its time trying to simplify fractions with a 1000 digit numerator and denominator. [1] https://www.stackage.org/haddock/lts-20.10/base-4.16.4.0/Prelude.html#t:Rational https://www.stackage.org/haddock/lts-20.10/base-4.16.4.0/Pre...
- 4y ago
- jacobmartin 4y agoThis is great! There was a post on /r/softwaregore recently where someone showed a progress bar on Steam that said "57/100 achievements! Game 56% complete" or something like that. I snarkily commented something about naively using floor() on floating point and then moved on. But then I thought that that may not have been the problem, fired up emacs and wrote a C program basically just saying printf("%f\n", 57.0 / 100.0 * 100.0); To my surprise this correctly gave 57.000000, but in python, 57 / 100 * 100 Gave 56.999... anybody know what was up here? Different algorithms for printing fp?
- lifthrasiir 4y agoThe calculated number is indeed 56.99999999999999289457264239899814128875732421875 which is not same to 57 (IEEE 754 binary32 or binary64 can represent this integer exactly). You can verify this with the following: printf("%f\n", 57.0 / 100.0 * 100.0 - 57.0); It just happens that `%f` in C defaults to 6 fractional digits.
- jacobmartin 4y agoThank you!
- creeble 4y agoCompiler optimization.
- lifthrasiir 4y agoCompilers are very cautious about floating point optimization because many real number identities do not hold in FP, so they won't do much unless instructed to do so. Even if they do a constant propagation they will generally calculate in binary, in decimal. (Exception: decimal literals in some languages like Go are untyped and can be exactly calculated before getting rounded at once.)
- kazinator 4y agoWrong guess; the algebraic optimization you might be thinking of is simply not allowed. The expression is likely subject to an optimization known as constant folding: the compiler calculates the value of the constant expression, and substitutes that value into the code. However, that constant-folding calculation has to produce the same result as what would happen at run-time: the 56.999999999999992895... approximation of 57. Constant-folding having to produce the same results as run-time creates a challenge in cross-compiling situations, when the host machine's math is different from the target machine's math. The compiler must emulate the target machine math.
- rkagerer 4y ago"The short answer is that 0.1 + 0.2 lies exactly between 2 floating point numbers, 0.3 and 0.30000000000000004, the answer is 0.30000000000000004 because its significand is even."
- hgsgm 4y agoThat's a poor summary. It's round(0.1) + round(.2) that sits between 0.3 and 0.3000...4 (rounding in base 2)
- jwmoz 4y agotldr; use Decimal
- jacobsenscott 4y agoThe proper tldr is - understand how floating point works (you can represent a huge range of numbers with a lot of precision, but not perfect accuracy, and high performance), and then decide if it is the proper data type for your problem. For example setting the heading and velocity of a missile (85.623 degrees, 500.138 m/s (idk how fast missiles go) floats are great. It is impossible to steer on an exact course anyway due to wind, temperature, etc (I assume...) Storing the number of dollars in you savings account - us decimal or better yet integer (just count the pennies).
- kazinator 4y agoIEEE 64 bit double can represent a missile velocity of 85.6230000000000.
- undershirt 4y agoFrom a deleted comment I liked here from @stabbles: > some things can be represented in finite digits in base x but require infinite digits in base y. Very good summary. Binary to decimal is very straightforward until fractions require infinite digits. I don’t think dec64[1] is even a tradeoff—it’s just better. The significand stays a normal binary number— but it encodes the decimal point in gasp decimal. No infinities required for the numeric language that we all think in. [1] https://en.wikipedia.org/wiki/Decimal64_floating-point_format https://en.wikipedia.org/wiki/Decimal64_floating-point_forma...
- lifthrasiir 4y ago> ... dec64 is ... Not to be confused with Douglas Crockford's DEC64 [1], which I believe is worse than binary floating points. [1] https://www.crockford.com/dec64.html https://www.crockford.com/dec64.html
- undershirt 4y agoOh, thank you! I actually think I’m going with Crockford on this one, and that’s what I meant to post.
- lifthrasiir 4y agoIn which case I disagree ;-). Most strikingly DEC64 doesn't do normalization, so comparison will be a nightmare (as you have to normalize in order to compare!). He tried to special-case integer-only arguments, which hides the fact that non-integer cases are much, much slower thanks to added branches and complexity. If DEC64 were going to be "the only number type" in future languages, it had to be much better than this.
- undershirt 4y agoGood points! I think decimal64 doesn’t normalize the significand also. But I can’t assess what I haven’t used. Mine is a snap judgment in favor of understanding more of dec64 vs the wiki article. My general feeling is that it’s time for the scale of computing to tip away from total correctness and efficiency, and more toward non-awkward interfaces. But at bottom, I would try both and then talk about it.
- tarotuser 4y agoBecause .3 cannot be described by a float exactly. And C/C++ doesn't have binary coded decimal natively. If it did, then much of what we use decimals and fractions for would be easier to show in decimal.
- peteri 4y agoI'm sure multiple variants have been implemented, Borland C++ V2.0 from 1991 (version chosen as it's on bitsavers.org) has a bcd type which IIRC uses the 80 bit 8087 packed decimal type but it's been a long time since I looked at this deeply.
- virgulino 4y agoI remember we had Borland Turbo Pascal 3.0 with BCD in the mid 1980s.
- yalogin 4y agoWhy isn't the floating point version of 0.1 just 0.10000000000000000000000000000000000000000000000000000000000000000000000000000000 and something like 0.10000000000000000555111512312578270211815834045410156250000000000000000000000000 ?
- croes 4y agoThat's why https://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/ https://www.exploringbinary.com/why-0-point-1-does-not-exist...
- bookofjoe 4y agoI have no idea what a floating point is but I still enjoyed reading the comments. I bet there are many people like me who love HN but choose not to make their presence known so as not to be downvoted (full disclosure: I've never down- or up-voted. But I do know this: on the rare occasions I remark on how funny or clever I found something, that comment usually gets downvoted).
- nixpulvis 4y agoThink of floating point as a "floating" "point" like the dot that separates the two parts of a number like 3.1415. It floats around because this allows you to have more precision when the number is small without giving up the ability to represent large number. Fixed point number encodings also exist and are much simpler, e.g. use 24 bits for the left hand side and use 8 bits for the decimal part. Anyway, thought I'd give you some background. This stuff is easy to look up too. You're probably getting downvoted because HN doesn't really like unsubstantive comments.
- ThrowawayR2 4y ago> "...on the rare occasions I remark on how funny or clever I found something, that comment usually gets downvoted" Not unjustifiably; without specifically referring to any posts you may have made, such comments in general add no insight or value to the discussion. It's just noise that has to be scrolled past. (The same goes for discussing getting downvoted, which is discouraged by the HN guidelines.)
- weberer 4y agoComputers store numbers in base 2 rather than base 10. That means each digit is no longer the ones/tens/hundreds place, but instead the ones/twos/fours/eights place. This is all fine when talking about integers, since every integer in base 10 can be represented in base 2. The problem is when converting decimals. Now instead of each digit being tenths/hundredths/thousandths, we have halfs/fourths/eights/etc. Now try out the problem yourself. Imagine you had a formula in the form of (1/2)x + (1/4)y + (1/8)z + (1/16)a + (1/32)b ... Try to find a solution that adds up to exactly 0.1 and you'll see that it can't be done. Computers just get as close as they can.
- JJMcJ 4y agoAt first I was expecting an ill informed rant, until I saw it was Julia Evans, who always digs down and then explains a subject with great clarity.
- alexjplant 4y agoI once inherited the development and maintenance responsibilities on a financial forecasting/business development app. There were lots of expected value computations in the form of adding up potential revenues multiplied by their probability of win with costs subtracted (i.e. currency values). The previous developer had used floats everywhere resulting in runoff values as seen here. I started using decimal in all of the new features in an effort to mitigate this but it resulted in even more headache as I now had a bunch of casts floating around on top of the truncation band-aids that I had to implement for existing lengthier calculations. My plan was to refactor the whole app and SQL schema but if memory serves I got pulled onto something more pressing before I had the chance. This was especially disappointing to me because this was all implemented in C# and T-SQL which are languages with first-class support for decimal numbers. It wouldn't surprise me if the app is still in use today with some hapless dev halfway across the country whacking these bugs as they pop up.
- parhamn 4y agoThis makes me wonder why Decimals aren’t part of std libs in Javascript or Golang.
- lifthrasiir 4y agoIt is hard to design. There should be some sort of precision and rounding mode control throughout calculation, meaning there should be some sort of implicit state or those controls should be sprayed into every operation. The latter is explicit but tedious [1]. The former will still surprise people from time to time. IEEE 754 binary number is sort of working well in this aspect. [1] See QuickJS's BigDecimal extension for example: https://bellard.org/quickjs/jsbignum.html#Properties-of-the-BigDecimal-object https://bellard.org/quickjs/jsbignum.html#Properties-of-the-...
- ClumsyPilot 4y agothat's eactly why it needs to be in standard library- so people dont make mistakes reinventing the wheel
- TheRealPomax 4y agoMandatory link to the original "What Every Computer Scientist Should Know About Floating-Point Arithmetic" by David Goldberg over on https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.h...
- jordigh 4y agoThat url must have the highest linked-to-read ratio on the internet. Although it seems to be fading away, linked to less frequently.
- deleted 4y ago[deleted]
- rafaelturk 4y agoFor everyone working on tech... When your friends mock you, and make funny comments how tech is hard and often unpleasant to use this a perfect example. This thread have multiple `techy` examples, how binary numbers works, etc. Thurth is the year is 2023 we have CPUs with billions of transistors and we still have fundamentaly basic bugs like math. I really don't understand why languages dont default to decimals for literals..
- ClumsyPilot 4y agobecause some developers hate anything that makes thongs easier
- zokier 4y agoAll number formats have tradeoffs, nothing can robustly represent all real numbers. At least floats are standardized and their probmems relatively well characterized and understood.
- int_19h 4y agoThe problems with decimal rounding are even better understood and standardized, given that we've been working with decimals long before we had binary computers.
- esoterica 4y agoYou realize that decimal rounding has the literal exact same type of problems as binary rounding right? It’s literally just a different base.
- int_19h 4y agoI do realize that. The point is that humans are familiar with the typical manifestations of those problems in decimal representation, because that's what we use day to day. Every kid who had a calculator to play with while they were bored knows what 1/3 does. They also know that 0.1 + 0.2 gives exactly 0.3. The problem isn't binary itself, even. It's that our tools pretend that it's decimal, misleading people. If it looks like decimal, it should behave as one by default.
- raldi 4y agoThe entire intrigue of the opening paragraph is the claim: > there’s a floating point number that’s closer to 0.3 than 0.30000000000000004! But then you read to the end and find out this wasn’t true, and the answer to the clickbait headline is just, “Because it’s the closest floating-point value to the correct answer”.
- topaz0 4y agoYou're mistaken. In fact 0.3 is closer to the next-smaller float than it is to the next-larger one which results. But the result of float(0.1) + float(0.2) is not any of those things, but rather halfway between the previous and next floats from 0.3, so it gets rounded up instead of down.
- ninepoints 4y agoWow the "we should be using decimal" takes in this thread are hilariously misguided.
- mensetmanusman 4y agoHard mode: what application would be effected by .3 being off 10^-17
- insane_dreamer 4y agoan aside (to Julia's always excellent explanations) - it sure would be nice if python had first class support for decimals :/
- dennis_jeeves1 4y agoSimple explanation: because computers do not 'understand' the concept of decimals. 'Natively', they can only manipulate integers.
- picture 4y agoFor those who prefer a more visual and interactive demo: https://evanw.github.io/float-toy/ https://evanw.github.io/float-toy/ It's not very complicated when you see it in bits
- dgudkov 4y agoBoth Excel and Google Sheets return FALSE for this expression: 2.03 - 2 - 0.03 = 0 The vast majority of data transformation and BI tools (Power BI, PowerQuery, Tableau, etc.) return FALSE for this expression: 0.1 + 0.2 = 0.3 That's because they use floats instead of decimals and that introduces subtle errors in data. These errors usually never get noticed because everyone doesn't expect errors in basic math. It's a mystery to me why most commercial software intended for business and financial calculations don't use fixed point decimals. My post about this: https://www.linkedin.com/feed/update/urn:li:activity:7028101503949168641/ https://www.linkedin.com/feed/update/urn:li:activity:7028101... PS. If you design software that works with money amounts, always use fixed point decimals. Don't use floats, it's just wrong!
- paddim8 4y agokalker.xyz seems to return true
- CamperBob2 4y agoPS. If you design software that works with money amounts, always use fixed point decimals. Don't use floats, it's just wrong! Eh, doubles are fine for (most) currencies. Just don't do comparisons without appropriate epsilons. People who compoare floating-point numbers for equality are going to make other fundamental mistakes with whatever data type you force them to work with.
- worik 4y agoYes But actually, since fixed point decimals are not all ways available use the smallest unit of account, not smallest legal tender, and integer arithmetic And learn how to round
- adamwk 4y agoSmallest unit also has issues. For instance the Indonesian rupiah technically is made up of 100 sen, but the currency is so inflated nobody uses it and currency libraries behave differently (even different versions of the same library). We had a bug where different OS versions provided different values when normalizing it to a smallest unit integer. If you really don’t have access to a decimal type I think the best solution is to convert it to micro-units (price * 10^6). This is what Android does in its billing library.
- McGlockenshire 4y ago> I think the reason that 0.1 + 0.2 prints out 0.3 in PHP is that PHP’s algorithm for displaying floating point numbers is less precise than Python’s It's a display thing, not an algorithm thing. It rounds by default at a certain length, previously 17 digits. php > echo PHP_VERSION; 8.2.1 php > $zeropointthree = 0.1 + 0.2; php > echo $zeropointthree; 0.3 php > ini_set('precision', 100); php > echo $zeropointthree; 0.3000000000000000444089209850062616169452667236328125 https://www.php.net/manual/en/ini.core.php#ini.precision https://www.php.net/manual/en/ini.core.php#ini.precision
- georgeburdell 4y agoThis is one of my favorite interview questions. I embed the issue in a short block of code, run it, and ask the candidate to explain the what, why, and how to fix. I work in a field with physical measurements. Maybe 1/3 of candidates get it correct
- davbryn 4y agoWhat do you gain from asking this question in an interview? Let's be honest, you've spotted it online and used it to show that you have a higher understanding. If I had an interviewer ask me this it would be a massive red flag. You are interviewing a candidate - it isn't the place to recycle someone else's online answer to flex
- georgeburdell 4y agoNo, we’ve actually had this issue in production. I did not “spot it online”. The question I ask is framed how the bug appeared for us at the time. It’s a great way to screen out people who are too academic and do not consider the limitations of the systems they use.
- dennis_jeeves1 4y agoYou asked an issue that _you_ faced in prod, you have the benefit of hindsight. I'm assuming that you are interviewing candidates where this bug is not common.
- whywhywouldyou 4y agoThis isn't a competition. They aren't in a classroom where the professor is asking a trick question. This person wants to hire someone who can spot the bug, understand it, and propose solutions. Who cares if the candidate comes from a field where this isn't common? That's the point. You want to hire people who would know what to do if they came across it. It's amazing the amount of hostility here towards a perfectly sensible interview question and process.
- 4y ago
- worik 4y agoComputers, as we use them, to operations on finite sets. Real numbers are simulated using a finite set of bits. So equality comparisons are not useful for floating point numbers This is computing 101
- ColinDabritz 4y agoI love that this is a common enough problem, that there's a full domain website for it: https://0.30000000000000004.com/ https://0.30000000000000004.com/
- blackflame7000 4y ago[dead]
- pantulis 4y agoAnd that's the reason why Decimal types are your friend, kids.
- mharig 4y agoHopefully, we will have posits with hardwaresupport sooner than later. https://spectrum.ieee.org/floating-point-numbers-posits-processor https://spectrum.ieee.org/floating-point-numbers-posits-proc... https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/posits/ https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/posits... Unfortunately, they are not a solution to the OPs problem, which is fundamentally embedded in the architecture of computers. One has to find an appropriate representation for the needed numbers in bits and bytes. In Python one can use fractions or decimals, if the float format is not good enough. Other options are fixed point arithmetic or arbitrary precision arithmetic. Choose one that combines the needed characteristics with the least amount of work.
- anthk 4y agoTest with Unicon: procedure main() if (0.1 + 0.2 == 0.3) then { write("true") } end It prints "true".
- bradwood 4y agoBecause mantissa and exponents CS101 -- can we please move on.
- deleted 4y ago[deleted]
- evdubs 4y agoIt doesn't have to $ racket Welcome to Racket > (read-decimal-as-inexact #f) > (+ 0.1 0.2) 3/10 Lovely.
- simonblack 4y agoIt doesn't. That might be number as returned from your software but it is inexact.
- cratermoon 4y agohttps://0.30000000000000004.com/ https://0.30000000000000004.com/
- hansvm 4y agoI really like biginteger rational types when working with typical non-scientific non-integer problems, like classical accounting. Everything continues to work without a hitch when somebody decides they need to support a tenth of a cent (gas stations), they need to support a tax like a third of a percent, they need to seamlessly mix with smaller units like satoshis, .... It's a solution that never fails (by hypothesis, you aren't working with sin or pi or gamma or some shit), and for every practical input the denominator stays small enough that it fits nicely into machine registers. You have the bigint fallback for the edge cases, in case they're needed. Floats IMO are only suitable when you're actually okay with approximate results (perhaps including known convergence properties so that you can reify an ostensibly floating point result into more exact values).
- martyvis 4y agoWhy does the title miss the key words "sum of" or the symbol "+"?
- deleted 4y ago[deleted]
- jaxrtech 4y agoI still kills me that (correct me if I'm wrong) the original version of SmallTalk on the Xerox Park Alto had a native arbitrary precision Fraction type.
- WalterBright 4y ago> Why does 0.1 and 0.2 = 0.30000000000000004? Because we have 10 fingers rather than 8.
- rerdavies 4y agoBy the time you finish reading this thread, you could have learned how floating-point numbers work, and wouldn't have to ask (or opinionate). :-P
- gjadi 4y agoYou can use Herbie to increase the accuracy of your floating point operations. From the tutorial: Herbie rewrites floating point expressions to make them more accurate. Floating point arithmetic is inaccurate; even 0.1 + 0.2 ≠ 0.3 for a computer. Herbie helps find and fix these mysterious inaccuracies. https://herbie.uwplse.org/ https://herbie.uwplse.org/
- bestprogmaram 4y agobecause its right
- bestprogmaram 4y agobecause it is right
- fennecfoxy 4y agoBecause JavaScript hurr durr
- rolenthedeep 4y agoI'd love to see an in depth exploration of exactly how float imprecision happens. Why is the error at 4e-10 and not 3? What binary magic is causing these spurious results? We've all been told about float imprecision in a very hand-wavy way that doesn't actually explain anything at all about the problem. On its face, one would assume that any two binary values being added would have a reliable and deterministic value, but that's not how floats work. There's some magic in the stack that causes errors to creep in, which isn't something we see with other types of numerical representation. It's very easy to show and understand that 01b + 01b = 10b, but that logic somehow doesn't apply to floats. I think it's a very interesting subject and I wish there was more discussion of the real causes rather than just "oh yeah, floats do that sometimes, just ignore it"