21 ms·
9999999999999999.0 – 9999999999999998.0
- f2f 8y agoAlso worth checking: http://0.30000000000000004.com/ http://0.30000000000000004.com/ most popular previous discussion: https://news.ycombinator.com/item?id=10558871 https://news.ycombinator.com/item?id=10558871
- gpm 8y ago2 with 64 bit floats, 0 with 32 bit floats.
- sp332 8y agoI could understand 0, but how does it get 2?
- twtw 8y agoThe nearest doubles to each of these two decimal constants end up being roughly 2 apart. Whereas for fp32 both decimal constants are stored as the same float.
- Dylan16807 8y agox999 rounds up when stored, x998 stays the same.
- theoh 8y agoThese integers are so large that they cannot be precisely represented by 32 bit or 64 bit floats. So there's a rounding effect. https://stackoverflow.com/a/1848953 https://stackoverflow.com/a/1848953
- your-nanny 8y agobecause 2 is the interval of precision at that scale. In floating point loss of precision scales with magnitude.
- codeflo 8y agoFP numbers are (roughly) stored in the form m×2^e (m = mantissa, e = exponent). When numbers can't be represented exactly, m is rounded. My guess is that these numbers end up being encoded as 4999999999999999×2 and 4999999999999999.5×2, where the latter is rounded up to 5000000000000000×2.
- tomxor 8y agoBecause 9999999999999999 is rounded to 10000000000000000 before any math even happens. Precision != order of magnitude.
- anothergoogler 8y agoDAE mind blown by imprecision in float arith? Edit: Down-Voters go ahead and explain the LIE that is computer accuracy in the face of the linked demonstration. Computers, can't trust em.
- throwawaymath 8y agoI didn't downvote you, but this isn't a problem with computers. It's a problem with the (mis)use of floats. Floats are not decimals. That's unfortunately a really, really common misconception, owing in part to poor education. Developers reach for floats to represent decimals without thinking about the precision ramifications. When you're working with decimals that don't need a lot of precision this doesn't generally come up (and naturally, those are the numbers typically used in textbooks). But when you start doing floating point arithmetic with decimals that require significant precision, things get bizarre very fast. Unfortunately if a developer isn't expecting it, that's likely to happen in production processing code at a very inopportune time. But the computer is just doing what it's told - we have the tools to support safe and precise arithmetic with decimals that need it. It's a matter of knowing how and when to use floating point.
- Sharlin 8y agoFWIW, a fixed-precision floating-point decimal type would have the same problem. At some point the spacing between two consecutive floating-point values (ULP [1]) simply becomes more than one, no matter the radix. [1] https://en.wikipedia.org/wiki/Unit_in_the_last_place https://en.wikipedia.org/wiki/Unit_in_the_last_place
- jackfraser 8y agoYou're probably being downvoted for posting like you're on some other site, moreso than your sentiment that this is just a simple CS 101 thing that people ought to know. Thing is, a lot of people don't take CS courses, and have to learn this as they go along. More importantly, the naive cases all seem to work fine - it's only when you get to increasing precision / scales that you notice the cracks in the facade, and that's only if you have something that depends on the real accuracy (e.g. real world consequences from being wrong) or if someone bothers to go and check (using some other calculator that gives more precise results). My own view on it is that it's past bloody time for languages to offer a fully abstracted class of real numbers with correct, arbitrary precision math - obviating the need for the developer to specify integer, float, long, etc. I don't mean that every language should act like this, but ones aimed at business software development, for example, would do well to provide a first-class primary number type that simply covers all of this properly. Yes, I can understand that the performance will not be ideal in all cases, but the tradeoff in terms of accuracy, starting productivity, and avoiding common problems would probably be worth it for a pretty big subset of working developers.
- gravypod 8y agoJava also has BigDecimals for this kind of work.
- carterschonwald 8y agoits good to educate people about default representations of numerical literals and their corner cases. Whenever I see these examples I do get annoyed st the Haskell one because we never are told what type it gets defaulted to, which only happens silently in the ghci repl, but will trigger a warning if it’s in a source file that’s being compiled.
- misterdoubt 8y agoUsing one of these workarounds requires a certain prescience of the data domain I'm a little concerned if merely knowing the existence of floating point arithmetic constitutes "prescience."
- alanfranz 8y agoWhat is the "right answer"? Is the article claiming that such languages don't respect IEEE-754, or that IEEE-754 is shit? If you want arbitrary precision, use an arbitrary precision datatype. If you use fixed precision, you'll need to know how those floats work. Pointless article, imho.
- mabbo 8y agoThe point is to illustrate a simple fact that most of us know- but maybe some don't. https://m.xkcd.com/1053/ https://m.xkcd.com/1053/
- twtw 8y agoIt doesn't even illustrate that particularly well. As is, the page just seems to be pointing at floating point and yelling "wrong", with no information on what's actually happening. By all means embrace the surprise and educate today's 10,000, by why not actually explain why these are reasonable answers and the mechanics here behind the scenes?
- jacobtwotwo 8y agoI was one of those people today! This intrigued me enough to learn more about IEEE 754. Thank you for the relevant xkcd!
- Retric 8y agoThere are several. If you subtract two numbers close to each other with fixed precision you don’t know what the revealed digits are. (1000 +/- .5) - (999 +/- .5) = 1 +/- 1. Thus 0, 1, and 2 are all within the correct range.
- preinheimer 8y ago$ php -v PHP 7.2.10 (cli) (built: Oct 9 2018 14:56:43) ( NTS ) $ php -r "echo 9999999999999999.0 - 9999999999999998.0;" 2 $ php -r "echo bcsub('9999999999999999.0', '9999999999999998.0', 1);" 1.0 bcmath - http://php.net/manual/en/function.bcsub.php http://php.net/manual/en/function.bcsub.php
- blowski 8y agoWhat happens with the bcmath extension enabled?
- preinheimer 8y agogreat point! edited to add. Decimal looks like a good option in php7, but a bit past a command line implementation: http://php-decimal.io/#introduction http://php-decimal.io/#introduction
- deleted 8y ago[deleted]
- mikey_p 8y agoResult for all versions of PHP: https://3v4l.org/JYlrp https://3v4l.org/JYlrp
- mikey_p 8y agoAnd with bcmath: https://3v4l.org/AmOQt https://3v4l.org/AmOQt
- seanalltogether 8y agoAre there any mainstream languages that consider a decimal number to be a primitive type? I feel like floating point numbers are far less meaningful in every day programs. Even 2d graphics would be easier with decimal numbers. Unless you're using numbers that scale from very small to very large, like 3d games or scientific calculations, you don't actually want to use floating point.
- deleted 8y ago[deleted]
- mschuetz 8y ago> Are there any mainstream languages that consider a decimal number to be a primitive type Mathematica. But it's not particularely fast. > Unless you're using numbers that scale from very small to very large, like 3d games or scientific calculations, you don't actually want to use floating point. Unfortunately, we can sometimes only use floats in 3D graphics and floats aren't even good for semi-large to large 3D scenes. Unity is a particular bad offender. It's not even necessary for meshes but having double precision transformation matrices would make life so much easier. Could simply use double precision world and view matrices, then multiply them together and the large terms would cancel out in the resulting worldView matrix, which can then by cast back to single precision floats.
- MauranKilom 8y agoNot to mention z-fighting for distant objects.
- misterdoubt 8y agoDepends how you define 'primitive type.' A decimal number is built-in for C# and comes along with the standard libraries of Ruby, Python, Java, at least.
- lozenge 8y agoCobol?
- twtw 8y ago
- dsalaj 8y agoGoogle calculator gives answer 0 where as duckduckgo calculator answers with 2. xD
- azhenley 8y agoBing gives 1 though!
- sampo 8y agoSo duckduckgo uses the normal 64bit floating point, and the clever people at bing automatically switch to bignums when needed. But I have no idea what google does to get that 0?
- pdw 8y agoThat's disappointing. The Android calculator has an awesome arbitrary precision engine (implemented by Hans Boehm!). https://cacm.acm.org/magazines/2017/8/219594-small-data-computing/fulltext https://cacm.acm.org/magazines/2017/8/219594-small-data-comp...
- ivanhoe 8y agoInteresting thing is that Google calculator will give 2 if you fill in the numbers by clicking on the calculator buttons, instead of writing them in the search bar.
- svat 8y agoA useful website for these that I ran across recently: https://float.exposed/ https://float.exposed/ For example, entering 9999999999999999.0 into "double" gives https://float.exposed/0x4341c37937e08000 https://float.exposed/0x4341c37937e08000 and entering 9999999999999998.0 gives https://float.exposed/0x4341c37937e07fff https://float.exposed/0x4341c37937e07fff My wishlist for such a page would contain two additional features: 1. Allow entering expressions like "a OP b == c", so that one can enter "0.1 + 0.2 == 0.3" or "9999999999999999.0 - 9999999999999998.0 == 1.0" and see the terms on the left-hand side and right-hand side. 2. Show for each float the explicit actual range of real numbers that will be represented by that float. For example, show that every real number in the range [9999999999999999, 10000000000000001] is represented by 10000000000000000, and that every real number in the range (9999999999999997, 9999999999999999) is represented by 9999999999999998. The author of this one has a blog post about it: https://ciechanow.ski/exposing-floating-point/ https://ciechanow.ski/exposing-floating-point/ and I also like a shorter (unrelated) page that nicely explains the tradeoffs involved in floating-point representations and the IEEE 754 standard, by usefully starting with an 8-bit format: http://www.toves.org/books/float/ http://www.toves.org/books/float/
- scrollaway 8y agoOT: What a lovely tld .exposed is. I… really wonder about its majority userbase.
- deleted 8y ago[deleted]
- DougBTX 8y ago> 9999999999999999.0 into "double" gives https://float.exposed/0x4341c37937e08000 https://float.exposed/0x4341c37937e08000 Nice that it reformats the input to "10000000000000000.0", gets the point across that a 64 bit double float just doesn't have enough bits to exactly represent 9999999999999999.0, but that it does happen to be able to represent 9999999999999998.0.
- loeg 8y agoAn easy rule of thumb is each 3 decimal digits takes 10 bits to represent. 9999999999999999 is 16 (= 15 + 1) decimal digits. And 3 bits can only represent 0-7. So you need more than 3 bits for that final decimal digit. So, 50 + 4 bits. IEEE 754 64-bit floats have 53 significant bits ("mantissa").
- Dylan16807 8y agoNot that this is specifically a "floating point" problem. Plenty of languages are going to get upset if you add 2 billion to 2 billion.
- Sawamara 8y agoYeah, one more article shitting on IEEE-754, yaay.
- ddtaylor 8y agoInterestingly `bc` as well as `bash expr` on the command line give the correct result.
- mcguire 8y agobc uses arbitrary precision, not floatys.
- LeoPanthera 8y ago"bc - An arbitrary precision calculator language" bc is doing its job!
- nly 8y agoThis is particularly sucky to solve in C and C++ because you don't get arbitrary precision literals. #include <boost/multiprecision/cpp_dec_float.hpp> #include <boost/lexical_cast.hpp> #include <iostream> using fl50 = boost::multiprecision::cpp_dec_float_50; int main() { auto a = boost::lexical_cast<fl50>("9999999999999999.7"); auto b = boost::lexical_cast<fl50>("9999999999999998.5"); std::cout << (a - b) << "\n"; } works int main() { fl50 a = 9999999999999999.7; fl50 b = 9999999999999998.5; std::cout << (a - b) << "\n"; } doesn't, even if you change fl50 out for a quad precision binary float type.
- svat 8y ago> Even user-defined literals in C++11 and later don't let you express custom floating point expressions Note that in your code sample you're not actually using user-defined literals (https://en.cppreference.com/w/cpp/language/user_literal https://en.cppreference.com/w/cpp/language/user_literal). This works (based on on your earlier code sample and adding user-defined literals): #include <boost/multiprecision/cpp_dec_float.hpp> #include <boost/lexical_cast.hpp> #include <iostream> using fl50 = boost::multiprecision::cpp_dec_float_50; fl50 operator"" _w(const char* s) { return boost::lexical_cast<fl50>(s); } int main() { fl50 a = 9999999999999999.7_w; fl50 b = 9999999999999998.5_w; std::cout << (a - b) << "\n"; }
- nly 8y agoThanks, it's nice to be wrong! For some reason I had it in my head that you couldn't get the token as a char const* for floating point expressions...
- al2o3cr 8y agoThe arithmetic is correct - the problem is that "9999999999999999.0" isn't representable exactly. 9999999999999998.0 in IEEE754 is 0x4341C37937E07FFF "9999999999999999.0" in IEEE754 is 0x4341C37937E08000 - the significand is exactly one higher. With an exponent of 53, the ULP is 2 - so parsing "9999999999999999.0" returns 1.0E16 because it's the next representable number. Using one of these workarounds requires a certain prescience of the data domain, so they were not generally considered for the table above. Doing arithmetic reliably with fixed-precision arithmetic always requires understanding of the data domain. If you need arbitrary precision, you'll need to pay the overhead costs of arbitrary-precision: either by opting-in by using the right library, or by default in languages like Perl6 and Wolfram.
- rg3 8y agoNote in C you can get the correct result if you use long doubles, which normally go to 80 bits[1]: printf("%Lf\n", 9999999999999999.0L - 9999999999999998.0L); In my x86_64 computer it breaks when you add enough digits. At this point it started outputting 0.0 as the difference: printf("%Lf\n", 99999999999999999999.0L - 99999999999999999998.0L); With 63 bits for the fraction part you more or less get around 19 decimal digits of precision, and the expression above uses 20 significant digits. [1] https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format https://en.wikipedia.org/wiki/Extended_precision#x86_extende...
- twtw 8y agoI don't understand all the crap that IEEE 754 gets. I appreciate that it may be surprising that 0.1 + 0.2 != 0.3 at first, or that many people are not educated about floating point, but I don't understand the people who "understand" floating point and continue to criticize it for the 0.1 + 0.2 "problem." The fact is that IEEE 754 is an exceptionally good way to approximate the reals in computers with a minimum number of problems or surprises. People who don't appreciate this should try to do math in fixed point to gain some insight into how little you have to think about doing math in floating point. This isn't to say there aren't issues with IEEE 754 - of course there are. Catastrophic cancellation and friends are not fun, and there are some criticisms to be made with how FP exceptions are usually exposed, but these are pretty small problems considering the problem is to fit the reals into 64/32/16 bits and have fast math.
- lenticular 8y agoYeah, the limitations of FP are well-known to anyone who does much numerical work. Floating point numbers are the optimal minimum message length method of representing reals with an improper Jeffery's prior distribution. A Jeffery's prior is a prior that is invariant under reparameterization, which is a mandatory property for approximating the reals. In this case, it is where Prob(log(|x|)) is proportional to a constant. Thus, we aren't going to ever do better than floats if we are programming on physical computers that exist in this universe. There is a reason why all numerical code uses them. Best to learn their limitations if you are going to use them, otherwise use arbitrary precision.
- erik_seaberg 8y agoThere's no reason for every step of a computation to be confined to the same very small message length. And the necessary error analysis should be built into the language, preferably in the same "advanced users only, here be dragons" package as the imprecise types themselves.
- TFortunato 8y agoSo interestingly, processor makers are on the same page with you re: computations, and lots of processors can internally do computations in "extended precision", e.g. 80-bit floats, only converting to/from 64-bit doubles at the start and end of the computation. https://en.m.wikipedia.org/wiki/Extended_precision https://en.m.wikipedia.org/wiki/Extended_precision
- mbostock 8y agoIt’s nice that JavaScript has arbitrary-precision integers now. 9999999999999999n - 9999999999999998n === 1n
- rurban 8y agoIn perl5 it's easier: perl -Mbignum -e'print 9999999999999999.0 - 9999999999999998.0' 1 The BigFloat solution on the website is suboptimal.
- lizmat 8y agoIn Perl 6 even shorter than that: perl6 -e'print 9999999999999999.0 - 9999999999999998.0' 1
- buchanae 8y agohttps://play.golang.org/p/_EkSHUIrg1y https://play.golang.org/p/_EkSHUIrg1y
- buchanae 8y agoBut, https://play.golang.org/p/naE55o3_xFP https://play.golang.org/p/naE55o3_xFP I guess the first link is converting the float constants to ints at compile-time? (edit: oh, it's actually mentioned in the article. I should read more carefully)
- dan-robertson 8y agoIn gcc, there is software emulation for hardware floating point arithmetic so that compile time constants may be evaluated for any target architecture (even if the compiling architecture does not support that format). It seems go approximates this as “just evaluate with a high precision then convert to float” which is probably mostly fine but having arithmetic be different between compile time and run time seems likely to be not fun.
- Tempest1981 8y agoDo any of the languages mentioned give a compiler warning? To help educate?
- PeterisP 8y agoIMHO it's unfeasible, because the exact same situation as with 9999999999999999.0 (a literal that's impossible to represent accurately as double and will get rounded to something else) applies also to very common cases such as 0.1 (which can't have an exact binary representation at all) - adding a compiler warning for that will mean that the warning will trigger for pretty much every floating point literal.
- acjohnson55 8y agoI don't think that's quite true. For 0.1,the algorithm for printing back the number will still result in 0.1, but the same is not true for 9999999999999999.0, which comes back 1e16. So compilers could easily warn for this specific situation, where what I'd call the round trip value of the literal is broken. But I don't think such a warning would be all that helpful. How often do we use literals with 16 significant digits, expecting exact representation? The bigger gotcha here is catastrophic cancelation. This is the issue of an insignificant rounding error becoming much more significant due to subtracting of very nearly equal numbers. You can't generally detect this at compile time if you don't know all your numbers in advance (e.g. you're not working with only literals).
- jmiserez 8y agoYou can do abstract interpretion and calculate rounding precision at each line. The problem is that as soon as you have loops, you'll pretty much get that warning everywhere. Being sure that there is no cancellation is even harder, but possible in some cases. I'm sure there are better approaches, there's tons of research papers in that area.
- consp 8y agoI don't get it. Why is the author complaining? He requests 17 digits of accuracy which is not something you should be using floats for in any form. Just import/link a package which can do arbitrary precision arithmetic of your choice and pay the overhead price.
- lacey 8y agoWhile it is not the default literal type in Haskell, you can use coercion and the Scientific type to compute an (almost) arbitrary precision result. For example this prints 1.0 in the repl: import Data.Scientific (9999999999999999.0 :: Scientific) - (9999999999999998.0 :: Scientific)
- 8bitsrule 8y agoFor some reason, this -very old- situation doesn't seem to trouble enough quadrillionaire coders. Perhaps inflation will fix it. Or the first quadrillionaire AI.
- Aardwolf 8y ago> Several of the results surprised me. Did they surprise you? Well, the perl6 result surprised me, since that means it's using something more precise than double precision floating point :)
- lizmat 8y agoThey're called Rat (for Rational number): https://docs.perl6.org/type/Rat https://docs.perl6.org/type/Rat , which maintain precision until the denominator exceeds 64 bits: then they're downgraded to doubles. If you want to keep precision still at that level, you can use FatRat: https://docs.perl6.org/type/FatRat https://docs.perl6.org/type/FatRat
- _kst_ 8y agoIt surprised me too, since it's not what I got. $ perl6 --version This is Rakudo version 2018.03 built on MoarVM version 2018.03 implementing Perl 6.c. $ perl6 -e 'print 9999999999999999.0-9999999999999998.0;print "\n";' 2 $ (Incidentally, I would have used "say" rather than "print" with an explicit newline.)
- lelf 8y ago› perl6 -e '.say for $*PERL.compiler, 9999999999999999.0-9999999999999998.0' rakudo (2018.11) 1
- lizmat 8y agoIndeed, there was a bug with determining when to switch to floats, that was fixed by Zoffix in August: https://github.com/rakudo/rakudo/commit/fec1bd74f97e257d4c88673cd62fdcae39f587a3
- crankylinuxuser 8y agoSo.. Can someone better versed in the ways of system level programming tell me why we still use IEEE 754 exponential notation? Iv'e seen article after article of how "horrible it is". So, are there default libs to use Binary Coded Decimal (BCD) or something like that?
- TazeTSchnitzel 8y agoWe use it because it's fast, accurate and very useful for many applications. It's not horrible.
- chowells 8y agoIn fact, as discussed in another thread, it's the optimal representation for many purposes. The only problems are that some languages over-privilege them to the point where it's difficult to use alternatives, and some programmers don't understand them.
- ori_b 8y agoBecause ever other alternative is either variable size (which implies allocations, making itb orders of magnitude slower), or has the same problems, just in different places.
- aidenn0 8y agoBinary floating points are not for system level programming; they are for scientific programming, where the accuracy of the values is related to the magnitude of the values. 64 bit integers are actually good for a lot of things that floating point gets used for; coordinates already should never have been floating point (the accuracy of measurement is independent of the magnitude for coordinates), but you could represent the entire solar system in millimeters without overflowing 64 bit integers (compared to not even the entire earth in mm for 32 bit integers). Also, it's popular to blame javascript for all today's problems, but the double-precision floating point is the only number type it has, which has some effect on its use.
- Retra 8y agoYou need a fixed data size for good performance. If you use fixed precision, you get absolute nonsense when doing very common calculations like `tan(x)`. IEEE 754 was masterfully engineered to produce the fastest, most correct results for the most common operations. We use it because it's fantastic.
- zelon88 8y agoJust tried it in PHP and was given an answer of 2.
- spencerwgreene 8y agoThe author says, "That Go uses arbitrary-precision for constant expressions seems dangerous to me." Why? My thoughts: 1) more inefficient programs because encountering an arbitrary-precision expression requires arbitrarily large memory and computation, 2) more complicated language implementation.
- banthar 8y agoConstant expressions are evaluated at compile time. Compilation would suffer any eventual performance penalties. This probably makes the compiler simpler - no need to implement different arithmetic for different types & no need to guess the types. The dangerous bit is, that just extracting a variable from constant expressions might change the result slightly. That should not be a problem, unless you are depending on exact values.
- int_19h 8y agoThe dangerous part is that now the value of the computation can change slightly if it's no longer constant. So e.g. if it involves a named constant, and that constant then becomes a variable for some reason (e.g. because it now needs to be computed at runtime, because it varies from platform to platform), you can end up with broken code with no warning.
- geocar 8y ago> Why? Hint 1: Can you imagine ever moving a magic number from an expression into a constant?
- deleted 8y ago[deleted]
- NextHendrix 8y agoActually Wolfram|Alpha does give 1, but Mathematica 11.3 gives 2.
- rexpress 8y agoMathematica interprets the real numbers 9999999999999999.0 and 9999999999999998.0 as having machine precision. To work in arbitrary precision, you need a backtick after the number, followed by the number of significant digits. In this case, 9999999999999999.0`17-9999999999999998.0`17 does indeed return 1. https://reference.wolfram.com/language/ref/Precision.html https://reference.wolfram.com/language/ref/Precision.html
- x0 8y agosqlite> SELECT 9999999999999998.0 - 9999999999999999.0; -2.0 MariaDB [(none)]> SELECT 9999999999999998.0 - 9999999999999999.0; -1.0
- loeg 8y agoWell, duh. One of those numbers isn't accurately representable in IEEE 754 64-bit "double" values, which is likely what all of the failing languages use. If you want more than 53 significant bits, you need something wider than a 64-bit IEEE double. And anyone who cares about such precision knows this and will expect to use a higher precision library. (Or is an incredibly specialized math language, like Wolfram.) If you think having only 53 significant bits is bad, you should see how many bits common trigonometry operations lose in libm implementations. Another fun example: video games (historically) have thrown away even 53 significant bits for the higher performing 32-bit floats.
- thanatos_dem 8y agoInterestingly, SQLite gets it wrong, returning 2.0, but MySQL, MariaDB, Postgres, and Cockroach all get it right at 1.0 I guess this comes down to most of them having implementations of arbitrary precision decimals.
- zeroimpl 8y agoIn PostgreSQL, if you specify a decimal literal, it is assumed to be type NUMERIC (arbitrary precision) by default, as opposed to FLOAT or DOUBLE PRECISION. If you stored your values in table rows as DOUBLE PRECISION, you would of course get the wrong answer.
- deleted 8y ago[deleted]
- deleted 8y ago[deleted]
- elpakal 8y agoWelcome to Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1). Type :help for assistance. 1> let foo = 9999999999999999.0 - 9999999999999998.0 foo: Double = 2
- mark-r 8y agoIs floating point math broken? https://stackoverflow.com/questions/588004/is-floating-point-math-broken https://stackoverflow.com/questions/588004/is-floating-point... No, it's just that a lot of people don't understand its limitations.
- Ngunyan 8y agoIt is broken once outside of that limitation.
- chris_mc 8y agoWell, one answer is to use IEEE 1788 interval arithmetic, which will at least give you IEEE 754 high and low bounds for a calculation, rather than one answer that's clearly wrong. Otherwise, some inaccuracy is the trade-off for fast floating point calculations.
- issam01 8y agoPascal/Delphi gives the right answer: writeln(FloatToStr(9999999999999999.0 - 9999999999999998.0)); 1
- rscho 8y agoThere is interesting ongoing research on representing exact reals: https://youtu.be/pMDoNfKXYZg https://youtu.be/pMDoNfKXYZg
- Veedrac 8y agoQuick summary of the talk: A specialist number representation is made for exact representation of values in geometric calculations (think CAD). Numbers are represented as sums of rational multiples of cos(iπ/2n). Exact summation, multiplication and division (not shown) of these quantities are possible, and certain edge-cases (eg. sqrt) have special-case handling. The system was integrated into and tested on an existing codebase. The speaker was also one of the authors of Herbie, if other people remember that.
- combatentropy 8y agoPros and cons of the different ways computers can work with fractions: https://softwareengineering.stackexchange.com/a/167166/109343 https://softwareengineering.stackexchange.com/a/167166/10934...
- chaitanya 8y agoThere's an easier way to specify long floats in Common Lisp: use the exponent marker "L" e.g. 9999999999999999.0L0. No need to bind or set reader variables. That said, even in Common Lisp I think its only CLISP (among the free implementations) that gives the correct answer for long floats. CLISP: [1]> (- 9999999999999999.0L0 9999999999999998.0L0) 1.0L0 SBCL, CMUCL and Clozure CL: * (- 9999999999999999.0L0 9999999999999998.0L0) 2.0d0 The standard only mandates a minimum precision of 50 bits for both double and long floats, so there's no guarantee that using long floats will give the correct answer, as we can see. http://www.lispworks.com/documentation/HyperSpec/Body/t_short_.htm http://www.lispworks.com/documentation/HyperSpec/Body/t_shor...
- adrianhel 8y agoThe default should have been arbitrary precision bignums with easy opt-in for floats and ints of varying sizes. But oh well.
- exactly 8y agoExactly.
- tzury 8y agoWith python, I get 2 even when using Decimals. Python 2.7.3 (default, Oct 26 2016, 21:01:49) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> from decimal import * >>> getcontext().prec 28 >>> a=Decimal(9999999999999999.0) >>> b=Decimal(9999999999999998.0) >>> a-b Decimal('2') That is unexpected.
- minitech 8y agoThe issue is that 9999999999999999.0 == 10000000000000000.0. You need to pass a string: Decimal('9999999999999999.0')
- lelf 8y ago>>> Decimal('9999999999999999.0')-Decimal('9999999999999998.0') Decimal('1.0')
- maxnoe 8y agoThis is because you create a python float (64-bit) before passing this float to the Decimal class. >>> a = 9999999999999999.0 >>> a 1e+16
- Steve44 8y agoThe accounting software we use has a built in calculator which has a similar problem. 5.55 * 1.5 = 8.3249999999999.... 26.93 * 3 = 80.7899999999999.... I raised it with the supplier some time ago, they said it's just the calculator app and the main program isn't affected. Quite shocking that they are happy to leave it like this.
- guggle 8y agoHow do Perl, Wolfram ans Soup get the "right" (ahaha...) answer ? (I'm not familiar with these langages) Of course for others it should "fixable" where needed: ~ python3 Python 3.6.7 (default, Oct 22 2018, 11:32:17) [GCC 8.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from decimal import Decimal >>> Decimal('9999999999999999.0') - Decimal('9999999999999998.0') Decimal('1.0')
- garethrees 8y agoThe linked post is a bit poorly expressed, but I think there is a good point there: fixed-size binary floating-point numbers are a compromise, and they are a poor compromise for some applications, and difficult to use reliably without knowing about numerical analysis. (For example, suppose you have an array of floating-point numbers and you want to add them up, getting the closest representable approximation to the true sum. This is a very simple problem and ought to have a very simple solution, but with floating-point numbers it does not [1].) Perhaps it is time for the developers of new programming languages to consider using a different approach to representing approximations to real numbers, for example something like the General Decimal Arithmetic Specification [2], and to relegate fixed-size binary floating-point numbers to a library for use by experts. There is an analogy with integers: historically, languages like C provided fixed-size binary integers with wrap-around or undefined behaviour on overflow, but with experience we recognise that these are a poor compromise, responsible for many bugs, and suitable only for careful use by experts. Modern languages with arbitrary-precision integers are much easier to write reliable programs in. [1] https://en.wikipedia.org/wiki/Kahan_summation_algorithm https://en.wikipedia.org/wiki/Kahan_summation_algorithm [2] http://speleotrove.com/decimal/decarith.html http://speleotrove.com/decimal/decarith.html
- MauranKilom 8y agoDo note that UB on integer overflow is (at least nowadays) more of a compiler wish for optimization reasons than it is technically necessary (your CPU will indeed just wrap around if you don't live in the 80s anymore, but a C++ compiler might have assumed that won't happen for a signed loop index).
- jdhzzz 8y agoGroovy 2.4.5 gets it right: println(9999999999999999.0-9999999999999998.0) 1.0