4 ms·
There 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 it
by lifthrasiir 4y ago
There 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...
- lifthrasiir 4y agoThis is indeed the reason that Python didn't (initially) have rational numbers while its spiritual predecessor ABC had. [1] [1] https://python-history.blogspot.com/2009/03/problem-with-integer-division.html https://python-history.blogspot.com/2009/03/problem-with-int...
- runeks 4y agoVery interesting. Thank you for sharing this.
- scaredginger 4y agoYup, it's a terrible idea, even if theoretically possible
- crdrost 4y agoI mean, cosine is pretty common... The next level solution is to apply generators so that either the decimal stream or the continued fraction is allowed to be infinitely precise, but I think this can have dangerous effects where checking whether a number is equal to 0 or maybe 1 can involve infinite computation? So that's where you really understand “oh, I do really need that epsilon, for comparisons’ sake.” For continued fractions I think you can also just have your library bound the size of the integers involved? So “it’s an array of signed int32s, but if your continued fraction generates a number that would overflow that, we just truncate the stream at that point.” Then the library is able to say that these two things are equal because their difference is [0; int_overflow] which becomes just [0]. Something like that.
- jfoutz 4y agojust yesterday someone commented about https://fredrikj.net/calcium/index.html https://fredrikj.net/calcium/index.html which is pretty darn amazing. pi and e are essentially first class, but a lot of transcendentals aren't. seems like a really neat approach.
- lifthrasiir 4y agoCalcium is amazing and so is exact real arithmetic or constructive real number, but they all can't avoid practically undecidable inputs. (Algebraic numbers as in Calcium can be made decidable, but they still can take an unreasonable amount of time to compute. Calcium does answer "unknown" for those cases.)
- scubbo 4y ago> many if not most mathematical functions won't be exact anyway That's actually a really interesting question - while this is obviously true for most functions which (in a mathematical sense) exist, I wonder if it's true for "all functions weighted by their use in computing applications"? That is - do boring old "addition, subtraction, and multiplication of integers" outweigh division, trigonometrics, etc.? In 3D modelling/video games, almost certainly not. In accounting software...probably? Across the whole universe of programs: who could say?
- lifthrasiir 4y agoNormally I would say that it is hard to tell, because it is. But I think in this particular case I have a reasonable argument---back in 2014 when Python added a support for matrix multiplication operator `@`, the proposal author did survey and made a case for it [1]. And you can see that an exponentiation operator `**` is actually used more than division `/` even in non-scientific usages. And as you've guessed, exponentiation won't be exact if its exponent is negative. [1] https://peps.python.org/pep-0465/#so-is-good-for-matrix-formulas-but-how-common-are-those-really https://peps.python.org/pep-0465/#so-is-good-for-matrix-form...
- scubbo 4y agoInteresting data, thanks! Since addition, subtraction, multiplication, and modulus are each used more than division and exponentiation _combined_ (and since not every use of those last two functions would result in an "inexact" result), I think we can pretty clearly conclude that "most usages of mathematical operators in these libraries will result in an 'exact' result" (I'm hand-waving on the definition of "exact", I don't think it's at issue here) Which is not, of course, a good justification for ceasing to worry about the problem, since a) those packages might not be representative of all libraries, and b) a small proportion of uses might result in a disproportionate amount of bugs.
- topaz0 4y ago> a small proportion of uses might result in a disproportionate amount of bugs. This is what concerns me. Sure, using decimal floating point solves 0.1 + 0.2 = 0.3 (which I can't imagine ever writing in real code). But if you get used to that, then you start to expect 0.1*x + 0.2*x to be 0.3*x, and depending what x is this may or may not be true. Maybe it works for all of your test cases (because your test cases are things like 2 and 10^-4), but then you accept some user input and start getting weird bugs (or infinite loops). There is no good solution besides expecting and preparing for rounding error.
- rqtwteye 4y agoRounding is fine but it would be nice if 0.1+0.2 was predictably 0.3. I am having a lot of trouble explaining to people that float numbers should be avoided unless you really need them. I have seen code that stored versions as floats and the dev was surprised that version 1.1 wasn't always equal to "1.1".
- lifthrasiir 4y ago> I have seen code that stored versions as floats and the dev was surprised that version 1.1 wasn't always equal to "1.1". And will break when the version reaches 1.10. While I agree we need a better way to teach this (e.g. inexact-exact distinction as in Scheme or more recently Pyret), that's as problematic as storing a telephone number as an integer (or worse, a FP number).
- rqtwteye 4y agoTotally agree that storing a version in a float is stupid but that's where we are :-(
- int_19h 4y agoAnd that's fine! People directly deal with math in decimal context, so we already have some expectations about how rounding etc works. So long as decimal type and its operations follow those expectations, they'll cope with it. The problem with binary is that these expectations don't translate for some of the most basic stuff.