12 ms·
0.30000000000000004
- seba_dos1 10y agoThat "digits=18" in R example probably bothers me more than it should.
- jwilk 10y agohttps://github.com/erikwiffin/0.30000000000000004/pull/107 https://github.com/erikwiffin/0.30000000000000004/pull/107
- lacampbell 10y agoI was literally wondering why R gave an "accurate" result for 0.2 + 0.1 the other night, despite using doubles. The printer has an algorithm to round then stringify
- imjustsaying 10y agoWhy does PHP produce 0.3 instead? edit: Not sure how I missed that. Thanks!
- arkitaip 10y agoPHP converts 0.30000000000000004 to a string and shortens it to "0.3". To achieve the desired floating point result, adjust the precision ini setting: ini_set("precision", 17).
- noonespecial 10y agoThird example in the article: PHP converts 0.30000000000000004 to a string and shortens it to "0.3". To achieve the desired floating point result, adjust the precision ini setting: ini_set("precision", 17). I remember years ago learning something like this and coming to a horrible realization : "My god, they're all strings!"
- okket 10y agoPrevious discussions: https://news.ycombinator.com/item?id=10558871 https://news.ycombinator.com/item?id=10558871 (1.5 years ago, 240 comments) https://news.ycombinator.com/item?id=1846926 https://news.ycombinator.com/item?id=1846926 (6.5 years ago, 128 comments)
- dxhdr 10y agoSomeone should figure out the mean length of time between article repeats on HN. And then find some correlation with university degrees and job tenure.
- deleted 10y ago[deleted]
- therealdrag0 10y agoDo you have a hypothesis about what the correlation might be?
- mrcactu5 10y agoI haven't seen it yet... what's with that final four (common to all languages)?
- nitrogen 10y agoFloats can only represent fractions of powers of two. 0.3 is a fraction of powers of two and five. See IEEE754 https://en.m.wikipedia.org/wiki/IEEE_floating_point https://en.m.wikipedia.org/wiki/IEEE_floating_point https://en.m.wikipedia.org/wiki/IEEE_754-1985 https://en.m.wikipedia.org/wiki/IEEE_754-1985
- mmarx 10y ago> See IEEE754 Only in the original 1985 version, however: IEEE 754-2008 actually specifies decimal floating point types; decimal arithmetic was already specified in IEEE 854-1987. Of course, the default representation is almost always one of the binary formats, but that is no longer the only option.
- santaclaus 10y agoThe C++ code could use std::numeric_limits<double>:: max_digits10 instead of the magical 17!
- YZF 10y agoRexx has decimal arithmetics so 0.1+0.2 is 0.3 ... http://www.rexxla.org/events/2001/mike/rexxsy01.PDF http://www.rexxla.org/events/2001/mike/rexxsy01.PDF
- hsivonen 10y agoUnsurprising considering the IBM affiliation of Rexx and decimal arithmetic being IBM’s pet issue.
- bsder 10y agoThe fact that we do binary floating point by default is an artifact of RAM being expensive way back in the day while arithmetic coprocessors became cheap. Single-precision is 4 bytes and can represent practically every quantity you care about precisely enough. While IBM has been harping about this for ages, BCD (binary coded decimal) was sufficiently important that it had special instructions on even the earliest microprocessors. The 6800/8080/Z80 series had DAA (decimal adjust accumulator) and the 6502 had SED/CLD (set/clear decimal mode). The 8008 is notable in not having a specific BCD instruction. And the 4004, which started it all, was a BCD oriented microprocessor from the start.
- spc476 10y agoAnd the 8086 (on up), the 68000 family, and the VAX also had BCD instructions. When I wrote my own 6809 emulator [1] the DAA instruction was the hardest one to get right [2] [1] https://github.com/spc476/mc6809 https://github.com/spc476/mc6809 [2] Specifically the condition code results.
- pcprincipal 10y agoAny time you're generating percentage data that should sum to 100, not appreciating floating point math will burn you. For those interested, the largest remainder method (https://gist.github.com/hijonathan/e597addcc327c9bd017c https://gist.github.com/hijonathan/e597addcc327c9bd017c) is useful for dealing with this.
- protonfish 10y agoUsing tricks to make numbers total what you think they "should" other than using rounding to proper amount of precision is lazy and deceitful.
- benjoffe 10y agoDepends on the application of this, i.e. whether the total is more important or the individual values are more important. Example: You're filling out a timesheet for a contracting job, and you worked 8 hours on several different tasks for your client, but your client's software rounds things to the nearest hour, then it would make sense to use an algorithm like this if your pay was going to be determined by this data entry. If your pay was not determined by this data entry then it may make sense to just round normally.
- jacobolus 10y agoYou should just round the percentages to the nearest value of the appropriate precision, and let them sum to slightly more or less than 100% if that’s how the numbers work out. If you want, add an asterisk and a “note, numbers do not sum to 100% because of rounding” at the bottom.
- pirocks 10y agoWhats going on in the second D example?
- deleted 10y ago[deleted]
- jwilk 10y agoThe "f" suffix means they are single-precision (32-bit) FP numbers.
- ayush--s 10y agoIt's stuff that should be a part of very beginning of any CS101. Float arithmetics != Decimal arithmetics
- derpadelt 10y agoIt is.
- deleted 10y ago[deleted]
- gnarbarian 10y agoOnly if you're trying to scare the newbies away. This stuff does need to be learned at some point but cs101 should be about getting a feel for programming in general. Focusing on the basics of objects/classes functions (with mandatory source control) is smart before killing their will to learn with nasty edge cases
- jonsen 10y agoI don't think fumbling around in a trial and error manner is the best start. The basic data types and the set of values they can represent should come early.
- Grue3 10y ago>Perl 6, unlike Perl 5, uses rationals by default I'm not sure this is such a good idea. I love rational datatype, but it's too easy to shoot yourself in the foot with simple numerical procedures resulting in gigantic bignum denominators.
- jacobolus 10y agoRational numbers are horrible for most numerical computing. The denominators double in length with every multiplication, so computation speed gets slow exponentially. Any time there’s any degree of uncertainty about a quantity (e.g. it comes from a physical measurement) there’s also no longer any advantage to using rational arithmetic. This turns out to encompass most practical situations. Rational arithmetic also breaks down entirely in the face of square roots or trig functions, unless you go for a fully symbolic computation environment, which gets even much slower. Rational arithmetic is mostly nice when the problems have been carefully chosen so the operations will stay rational and the answers will work out nicely, e.g. in high school homework.
- VLM 10y agodefault doesn't mean only, from memory Rakudo smooshes Rats into floating point Nums when the denominator gets above 2 to the 64th or so. FatRat are an infinite precision rat. FatRats, like RATs are Cool (which is a double entendre, because Cool means it knows how to be a string, which is apparently "Cool") The default recently has not been fatrat but one of the many benefits of a language under development for 20 years is that quite possibly that default was changed last weekend. Or maybe fatrat were default for a painful week back in '02. Probably not, but it could have happened. Perl does the right thing but if you're really unhappy about not using floats you can force a .num conversion into floating point or you could express a number in scientific notation to force floats. Perl really wants to make a division problem into a Rat unless you work hard to stop it. In general, for the past 30 years or so, if you're doing something weird, Perl will work and probably give faster and possibly more accurate results than most other tools, but the rest of the world will not be interested in debugging perl6 but will instead be asking "why you use perl6 instead of Gnu-R or mathematica or ?" or whatever is trendy momentarily today. Perl will never be trendy yet the whole world runs on it.
- msie 10y agoIt's actually pretty simple. When you have a base 10 system (like ours), it can only express fractions that use a prime factor of the base. In a way, not so simple (obvious to you? not to me)
- jacobolus 10y agoIt’s not obvious that 1/7 cannot be expressed in decimal, or 1/5 cannot be expressed in binary?
- bcook 10y ago>I thought people learned this sort of thing in grade school. Along with tact and general respect for others?
- jacobolus 10y agoThis is a discussion of the vagaries of floating point math, on a forum for programmers. I would expect that most folks learned something about how to carry out decimal long division in ~3th–6th grade sometime. If we can’t assume any kind of common base-line level of background experience but need to re-hash all of school mathematics in every conversation, then it’s hard to have a technical discussion. There’s absolutely nothing wrong with not having an understanding of this point, or forgetting grade school arithmetic, and I’m not trying to be condescending, but it seems weird for the top-level poster to call out the the article’s language. The idea that some fractions don’t have a terminating decimal expansion is “pretty simple” in the context of any reasonable standard for discussion among programmers. Here’s the language from the article. Is it really that confusing? > When you have a base 10 system (like ours), it can only express fractions that use a prime factor of the base. The prime factors of 10 are 2 and 5. So 1/2, 1/4, 1/5, 1/8, and 1/10 can all be expressed cleanly because the denominators all use prime factors of 10. In contrast, 1/3, 1/6, and 1/7 are all repeating decimals because their denominators use a prime factor of 3 or 7. In binary (or base 2), the only prime factor is 2. So you can only express fractions cleanly which only contain 2 as a prime factor. In binary, 1/2, 1/4, 1/8 would all be expressed cleanly as decimals. While, 1/5 or 1/10 would be repeating decimals. If the top-level poster was confused and wanted help, he/she could have said something like “Can someone explain this point to me? I don’t understand what it means for 1/7 to be a ‘repeating decimal’, or what a ‘prime factor of the base’ means.”
- mkup 10y agoOnce I wrote a library for double-to-string conversion and vice versa, which handles such roundings nicely: https://github.com/mkupchik/dconvstr https://github.com/mkupchik/dconvstr Key idea is not just to map binary floating point value X to a decimal floating point value Y, but instead (in extended precision, with 64-bit mantissa) compute an interval of decimal floating point values [Y1, Y2] which maps back to X (in standard precision, with 53-bit mantissa). Then choose such Y from [Y1, Y2] that Y has the shortest decimal representation.
- legulere 10y agoYou might be interested in interval arithmetics: https://en.wikipedia.org/wiki/Interval_arithmetic https://en.wikipedia.org/wiki/Interval_arithmetic
- jacobolus 10y agoPython does this, for example (but likely in a more efficient way): http://bugs.python.org/issue1580 http://bugs.python.org/issue1580 See also http://www.netlib.org/fp/ http://www.netlib.org/fp/ http://web.archive.org/web/20060908072403/http://ftp.ccs.neu.edu/pub/people/will/retrospective.pdf http://web.archive.org/web/20060908072403/http://ftp.ccs.neu... So for instance 0.29999999999999993 + 0.00000000000000003 -> 0.3, but 0.30000000000000002 -> 0.30000000000000004 Note that this will still not solve the 0.1 + 0.2 problem from the OP, since the nearest float to 0.3 is not actually the same as the nearest float to 0.1 + 0.2.
- amelius 10y ago> Note that this will still not solve the 0.1 + 0.2 problem from the OP, since the nearest float to 0.3 is not actually the same as the nearest float to 0.1 + 0.2. Interval arithmetic can be used, although that requires double the storage for all the intermediate results, and extra work. For example, .1 can be represented by x=[x0, x1], where x0 and x1 are floating point values bracketing .1, and similarly .2 can be represented by y=[y0, y1]. Then z=[z0, z1] can be computed by "z=x+y", where the interval z respects the sum of both intervals x and y. When printing, you can determine the smallest representation within the interval z. I'm not sure if it's worth all the trouble though :)
- smitherfield 10y agoMy personal view is decimal arithmetic should be the default and floating-point should be the library in scripting languages. And probably also Java/C#/Go-type languages.
- eru 10y agoWhy? Is decimal faster than arbitrary precision? If not, arbitrary precision should probably be the default (or binary).
- richdougherty 10y agoMy personal view is that the programmer needs to know the difference because there are important tradeoffs to consider. I do however think it should be easier to use decimal arithmetic. I've written financial apps in both Java and JavaScript and I wish both languages had native syntax and support for decimal numbers and arithmetic. JavaScript has nothing built-in and Java's BigDecimal class is clunky to use.
- adrianratnapala 10y agoDecimal only takes care of a subset of cases. I.e. those where the source data nicely lines up in decimal. Good for finance (although I think Britain made a mistake when it gave up the old penny and the shilling). But even there, you sometimes have to divide by numbers like 12 and 365. The point is NO representation is going to be idiot proof. So you just need to make a sufficient set of representations easy to use, and then try to teach the "idiots" what the underling issues are.
- int_19h 10y agoDecimal does only take care of a subset of cases, true. But 1) it's a very common subset, and 2) when it breaks down, people generally understand how and why, because they've dealt with it elsewhere.
- milesrout 10y agoNah, arbitrary-precision real arithmetic should be the default.
- lorenzosnap 10y agoI am surprised that domain was still available
- besselheim 10y agoMore interesting is exactly why floating point 0.1 + 0.2 != 0.3, and it's due to the way rounding is defined in the IEEE-754 standard: > An implementation of this standard shall provide round to nearest as the default rounding mode. In this mode the representable value nearest to the infinitely precise result shall be delivered; if the two nearest representable values are equally near, the one with its least significant bit zero shall be delivered. If we convert from decimal to double precision (64-bit) floating point, here is how they are represented in hexadecimal and binary: 0.1 -> 0x3FB999999999999A = 0011 1111 1011 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 0.2 -> 0x3FC999999999999A = 0011 1111 1100 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010 0.3 -> 0x3FD3333333333333 = 0011 1111 1101 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 ^^^^^^^^^^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ Taking 0.1 as an example, here is what its binary representation actually means: sign exponent mantissa (marked using ^ in the table above) 0 01111111011 1001100110011001100110011001100110011001100110011010 The exponent is encoded as its offset from -1023, so in this case we have 01111111011 which is decimal 1019, making the exponent 1019-1023 = -4. The mantissa (BBBB…) is an encoding of the binary number 1.BBBB…, so with an exponent of -4 that makes the actual number 0.0001BBBB…. Applying this for each of these numbers: decimal binary 0.1 0.00011001100110011001100110011001100110011001100110011010 0.2 0.0011001100110011001100110011001100110011001100110011010 0.3 0.010011001100110011001100110011001100110011001100110011 Then if we add 0.1 + 0.2, this is the result: 0.00011001100110011001100110011001100110011001100110011010 + 0.0011001100110011001100110011001100110011001100110011010 ------------------------------------------------------------- 0.01001100110011001100110011001100110011001100110011001110 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ However we only have 52 bits to represent the mantissa (again marked with ^), so the result above has to be rounded. Both possibilities for rounding are equidistant from the result: 0.010011001100110011001100110011001100110011001100110011 0.010011001100110011001100110011001100110011001100110100 <== ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ So according to the specification, the option with the least significant bit of zero is chosen. Converting this back to floating point format we get 0x3FD3333333333334. Note that the least significant four bits of the mantissa are 0100, which corresponds to the trailing 4 in the hexadecimal representation. This is not equal to 0x3FD3333333333333 (the result of conversion from decimal 0.3, and also what would have been the result here if the rounding was specified the other way.) Therefore, floating point 0.1 + 0.2 != 0.3.
- mcculley 10y ago> Your language isn't broken, it's doing floating point math. Yes, your language is broken if you have no way out of floating point math. JavaScript, for example, is fundamentally broken.
- jacobolus 10y agoThere are a huge pile of libraries for doing bignum, rational, fixed point, decimal, complex, vector, polynomial, symbolic, etc. math in Javascript. If you want you can even write one for 7-adic arithmetic, Eisenstein integers, numbers expressed in the golden ratio base, or points on the surface of a torus. There’s no language that can build in all possible number systems, and it’s not really the place of a language to try. It would be nice if JavaScript made a better type distinction between integers and floats though.
- mcculley 10y agoYes, every Turing complete language can build integers and other types of numbers atop floating point numbers if necessary. That doesn't make it any less broken.
- vmasto 10y agoAs a (kind of) related matter, thought it'd be interesting to mention JavaScript is most likely getting arbitrary precision ints soon https://github.com/tc39/proposal-integer https://github.com/tc39/proposal-integer
- haddr 10y agoFor this reason R has a function for comparing results of floating point math: all.equal() it is quite tricky as it might show that .1+.2 is 0.3, but this is only due to the fact that default output is a subject of default formatting (i.e. occulting more precision numbers)
- KC8ZKF 10y agoSBCL: * (+ (/ 1 10)(/ 2 10)) 3/10
- nneonneo 10y agoIt's great that your language of choice has fraction support - but real-world engineering problems rarely involve simple rational fractions exclusively. At some point you're going to run into roundoff error. Plus, fractional computing has severe problems with expanding denominators, which grow very quickly with non-trivial operands. Although computers are fast, the overhead of dealing with hundred-digit-long arbitrary precision integers eventually makes rationals unwieldy.
- pg314 10y agoIt's a trade-off between speed and accuracy. Not all software deals with real-world engineering problems, and there are plenty of applications where you would prefer accuracy. I'd rather make that trade-off myself than have the language decide for me. Most of the time I would start with correctness and if speed is needed and the profiler shows that the bottleneck lies in fractional arithmetic, I'll switch to floating point numbers. A similar trade-off exists with bignums versus 32 or 64 bit integers. There again, I prefer a language that uses bignums by default. I'll convert the bottlenecks to use fixnums after I've verified that it is safe to do so.
- Johnny_Brahms 10y agoWith lisps you can chose whether you want floating point or rationals.
- shp0ngle 10y agoNot sure if I like or dislike Perl6 decision to use rationals by default; so 0.1 is saved as 1/10 and 0.2 as 2/10 and the calculation is precise. But also possibly slow.
- macqm 10y agoC# is missing an example using decimal type: Decimal uses 128 bits to hold it's data and seems to handle rounding differently to System.Double, e.g. 0.1 and 0.2 using decimals evaulates to 0.3: > 0.1M + 0.2M 0.3 You can read more about decimal here: https://msdn.microsoft.com/en-us/library/364x0z75.aspx https://msdn.microsoft.com/en-us/library/364x0z75.aspx
- kirab 10y agoFor people using C or C++ I can recommend using decimal floating point (which may be added to C++20 in the standard). Contrary to the "default" floating point, which are base 2 and do not support precise presentations of certain numbers, you can use a decimal floating point system which uses base 10 and therefore allows exact and precise presentation and solves the problem mentioned on the page. Also important: since 2008 this is standardized in IEEE 754-2008 which added support for decimals. Explanation of decimal floating points: https://en.m.wikipedia.org/wiki/Decimal_floating_point https://en.m.wikipedia.org/wiki/Decimal_floating_point Libraries: https://software.intel.com/en-us/articles/intel-decimal-floating-point-math-library https://software.intel.com/en-us/articles/intel-decimal-floa... http://www.bytereef.org/mpdecimal/ http://www.bytereef.org/mpdecimal/ http://speleotrove.com/decimal/ http://speleotrove.com/decimal/ C++ Standard Proposals: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3407.html http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n340... http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3871.html http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3871.ht...
- milesrout 10y agoI absolutely disagree. If you want to represent currency, for example, you should not use decimal floating point. You should integers, specifically you should use an integer number of tenths of cents, which is pretty widely agreed as the standard unit of currency in a computer (or tenths of yen, for example). You need to be extremely careful about overflow, but you need to be anyway, and should almost certainly just use arbitrary-precision integers.
- kirab 10y agoI absolutely disagree with your disagreement. Please try writing an ERP system where you have a quantity of a billionth of an item price of a billion euros (or an item price of a billionth Euro and a quantity of billions) and tell me which integer type you deem sufficient for this. Additionally please research decimal floating points before disagreeing.
- deleted 10y ago[deleted]
- zeotroph 10y agoWhenver IEEE 754 and its quirks are discussed a potential alternative (but so far without hardware support) called Unum - Universal Numbers - should not go unmentioned: https://en.wikipedia.org/wiki/Unum_(number_format) https://en.wikipedia.org/wiki/Unum_(number_format) Previous discussions (>2y old): https://news.ycombinator.com/item?id=9943589 https://news.ycombinator.com/item?id=9943589 and https://news.ycombinator.com/item?id=10245737 https://news.ycombinator.com/item?id=10245737 A slideset: https://www.slideshare.net/insideHPC/unum-computing-an-energy-efficient-and-massively-parallel-approach-to-valid-numerics https://www.slideshare.net/insideHPC/unum-computing-an-energ...
- sn41 10y agoWilliam Kahan, who's the person mainly responsible for IEEE 754, has a critique of Gustafson's proposal: http://people.eecs.berkeley.edu/~wkahan/UnumSORN.pdf http://people.eecs.berkeley.edu/~wkahan/UnumSORN.pdf IEEE 754 is not a trivial standard (it earned Kahan a Turing award). Error modes for IEEE 754 are precisely defined, even though it requires a lot of effort to understand what they mean. (For example, overflow triggers an exception, but gradual underflow is allowed.) Going beyond it requires some serious effort, and unums seem not to be the solution. A good book to understand the IEEE 754 standard is the book by Michael Overton. The quoted article is unfortunately an example of floating-point "scaremongering". Floating point arithmetic is not approximate, it is accurately defined with precise error modes. Base-10 arithmetic is not however, the model to understand it, but rather "units in last place".
- dnautics 10y agoJust a heads up - we're moving on from the unums standard. https://youtu.be/aP0Y1uAA-2Y https://youtu.be/aP0Y1uAA-2Y
- jhanschoo 10y agoA note on the posit (aka sigmoid) standard that Gustafson discusses in the video: 1. Main feasibility benefit: behaves like IEEE 754 numbers in terms of accuracy. 2. Main performance benefit: able to match IEEE 754 precision & accuracy in fewer bits (~1/2) than IEEE 754. 3. When the standard is finalized, will specify behavior that in IEEE 754 allows for a degree of interpretation, meaning that behavior may not be reproducible across runs and machines. 4. IEEE 754 error-handling, that requires extra silicone or cycles, is not implemented. Overflow/underflow not considered errors leading to Inf or NaN, but rounded to nearest representation, hence has uniform treatment (rounding) across numbers with no exact representation. Illegal operations to be handled as general error à la integers rather than giving rise to NaN. 4a. Performance benefit: 4. should mean that less silicone or cycles required to perform arithmetic compared to IEEE 754, so Gustafson believes, though his team has only just finished with an ALU/FPU circuit schematic for posits. 4b. Programming using posits should feel more like programming using integers, versus handling NaN, Inf.
- faragon 10y agoBase 2 floating point is perfectly OK. The problem is people using floating point not knowing that IEE754 floating point representation is binary, and not decimal, so most decimal constants will not have an exact match. Said that, many implementations do buggy binary <-> decimal conversions, not because of the limit accuracy in the conversion, but because of "creative" rounding criteria.
- richev 10y agoI'm not sure why the round-trip formatter is used in the C# example (https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx#RFormatString https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110)....). If you omit it then you get the hoped-for result of 0.3.
- deleted 10y ago[deleted]
- dragonwriter 10y ago> Your language isn't broken, it's doing floating point math. Specifically, it's doing binary floating point math, which is an odd choice for decimal literals that's common only because very few languages choose correctness and clarity over performance when dealing with numbers, which leads to all kinds of problems with the most obvious approaches to addressing common problem domains in industrially popular languages. If I had a dollar for, well, every dollar of errors I'd seen because binary floating point values were used in a monetary calculation, I could afford to stop having to deal with them.
- Lazare 10y ago"If I had a dime for every time I've seen someone use FLOAT to store currency, I'd have $999.997634" https://twitter.com/billkarwin/status/347561901460447232 https://twitter.com/billkarwin/status/347561901460447232
- Exuma 10y agoHow does Postgres "work" properly? I was wondering this the other day. Is something magical going on behind the scenes that I should know about? I added a ::decimal to be safe, but I wasn't sure
- anarazel 10y agoIf you set extra_float_digits=3 then the output will always be large enough to contain the full precision. pg_dump et al. do so automatically.
- gsg 10y agoThe Common Lisp answer is 0.3 and not 0.30000000000000004 because those are single float literals: for double float literals, we have (+ 0.1d0 0.2d0) => 0.30000000000000004d0. I would imagine this is the case for several of the other examples, too.
- lispm 10y agoYou can tell Common Lisp to read double-floats by default. CL-USER 113 > (+ 0.1 0.2) 0.3 CL-USER 114 > *read-default-float-format* SINGLE-FLOAT CL-USER 115 > (setf *read-default-float-format* 'double-float) DOUBLE-FLOAT CL-USER 116 > (+ 0.1 0.2) 0.30000000000000005 Thus if one wants to make sure which float format Lisp should be reading, a) specify the default format, as above b) or use s, f, d, and l to specify the format (short float, single float, double-float, long-float). Example 1.0s0, 1.0f0, 1.0d0, 1.0l0
- kobigurk 10y agoI wonder if some of the examples exhibit fixed-point precision calculation, and if they do, it would be great to highlight it.
- learnaholic 10y agoFor all those interested in Floating Point Arithmetic, I'd strongly recommend reading this paper: http://www.lsi.upc.edu/~robert/teaching/master/material/p5-goldberg.pdf http://www.lsi.upc.edu/~robert/teaching/master/material/p5-g...
- PeterStuer 10y agoSide anecdote: Many, many years ago, I wrote a lease payment plan calculator for a big bank-linked leasing company when they moved from AS/400 to .Net. I made sure all rounding was policy driven, not only in precision, but also in where roundings would occur during the calculations. 1 cent differences do matter in such environments. Somehow it passed all the extensive regression tests that compared to their mainframe code out of the box with the config based on nothing more than my guesses. AFAIK the code is still in production to this date.
- xg15 10y agoThe fun thing is that in the case of an "accurate" result (0.3), you can't be sure whether the language arrived there by somehow doing accurate floating point math - or by following the PHP way of first rounding in base 2, then rounding again in base 10, thus making the result even more inaccurate.
- SagelyGuru 10y agoThe link given to Rust rationals is broken. It should be: http://rust-num.github.io/num/num_rational/ http://rust-num.github.io/num/num_rational/
- unfamiliar 10y agoThese examples all seem to be using double precision floating point, which might be misleading. For example, the C++ snippet is adding two double literals, not floating point literals. The result is even more inaccurate using single precision arithmetic: #include <iostream> #include <iomanip> int main() { std::cout << std::setprecision(17) << 0.1 + 0.2 << '\n'; // 0.30000000000000004 std::cout << std::setprecision(17) << 0.1f + 0.2f << '\n'; // 0.30000001192092896 }
- pvdebbe 10y agoHere's something that would probably help lessen the confusion: don't allow or atleast warn about implicit conversions in binary floats in source code literals. float foo = 0.3; // warning: invalid binary float value float bar = 0.3f; // ok, converts into nearest bin float decimal qux = 0.3m; // ok decimal feh = 0.3; // why not; will be exactly 0.3 All in all, languages go for decimal floats by default but then the implicit conversions ruin it for everybody who's still learning.
- septimus111 10y agoGood suggestion, but it still wouldn't prevent beginners from just learning the 0.3f notation as an idiom and skipping the explanation. The trouble with floats is that understanding them depends on knowledge of binary, scientific notation, etc. It's a lot for someone who is only just getting to grips with how a program works.
- deleted 10y ago[deleted]
- NicoJuicy 10y agoUse decimals in c# they are used for exact calculation ( and are slower) PS. It says: Console.WriteLine("{0:R}", .1 + .2); But when you do .1 + .2 it is 0.3 :) . With the 0:R he actually converts it to double which give the false result
- kazinator 10y agoTXR Lisp: > (+ 0.1 0.2) 0.3 Why is that? Because I made a "populist" decision purely for the sake of better "optics". The default printing precision for floating-point values is set at just enough digits to truncate things in this manner: 2> *print-flo-precision* 15 The cost to doing that is that the printed representation of floats isn't guaranteed to have read/print consistency: what we print doesn't always read back as the same value which in other words means that we don't have a unique printed representation for each possible value. That default of 15 isn't my favorite language design decision. It's justifiable and I believe in it, but not as wholeheartedly as in some other design decisions. In any case, language users who need to record every bit of the IEEE 754 double in the printed representation are not just hung out to dry: they can set or override this special variable to 17. 3> (let ((*print-flo-precision* 17)) (prinl (+ 0.1 0.2))) 0.30000000000000004 0.3 (One value here is the `prinl` output, under the dynamic scope of the special variable override; the other is the result value, printed in the REPL, with the top-level binding of it, still at 15 digits.) Oh, and by the way, we dohn't have to add 0.1 to 0.2 to demonstrate this. Either of those values by itself will do: 4> (let ((*print-flo-precision* 17)) (prinl 0.1)) 0.10000000000000001 0.1 5> (let ((*print-flo-precision* 17)) (prinl 0.2)) 0.20000000000000001 0.2 Why 15 is used the default precision rather than, say, 14, is that 15 still assures consistency in one direction: if any number with up to 15 digits of precision is input into the system, upon printing it is reproduced exactly in all 15 digits. And 15 is the highest number of digits for which this is possible with an IEEE 64 bit double.