Lies my calculator and computer told me (1987) [pdf]
stewartcalculus.com
stewartcalculus.com
Sure enough, slightly higher in the article: > “We use a circular reference in Excel to do linear regression.”
https://support.microsoft.com/en-us/office/use-goal-seek-to-...
https://support.microsoft.com/en-us/office/define-and-solve-...
# as shown in the text, fp math yields an inexact result.
# should be 3.310115e-5
$ python -c "from math import *; print(8721*sqrt(3)-10_681*sqrt(2))"
3.31011488015065e-05
# https://docs.sympy.org/latest/modules/evalf.html
$ python -c "from sympy import *; print(N(8721*sqrt(3)-10_681*sqrt(2)))"
3.31011506606020e-5
# the second argument to N is the desired precision
$ python -c "from sympy import *; print(N(8721*sqrt(3)-10_681*sqrt(2), 50))"
0.000033101150660602022280988927734905539317579147087675
# or using the somewhat more awkward decimal module:
$python -c "from decimal import *; print(Decimal(8721)*Decimal(3).sqrt() - Decimal(10_681)*Decimal(2).sqrt())"
0.00003310115066060202229
I'm not at all an expert at this, just wanted to play around with some tools that have helped me with similar problems in the past.I also don't know how accurate the digits following `3310115` are! All I know is that these are ways to make the machine spit out the correct number up to seven digits; I'd love it if somebody would explain to me more of what I've done here tbh
>>> Fraction("2/3") * Fraction("66/13")
Fraction( 44, 13)
Internally, SymPy uses mpmath (https://mpmath.org/) for representation of numbers to arbitrary precision. You could install and use the latter library directly, gaining extra precision without going through symbolic manipulation.
All that being said, it's still good practice to avoid loss of precision at the outset. Arbitrary-precision calculations are slow compared to hardware-native floating point operations. Using the example from mpmath's homepage in iPython:
In [1]: import mpmath as mp; import scipy as sp; import numpy as np
In [2]: mp.mp.dps=50 # set extended precision
In [3]: %%timeit
...: mp.quad(lambda x: mp.exp(-x**2), [-mp.inf, mp.inf]) ** 2
40.5 ms ± 4.74 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [4]: mp.quad(lambda x: mp.exp(-x**2), [-mp.inf, mp.inf]) ** 2
Out[4]: mpf('3.1415926535897932384626433832795028841971693993751015')
In [5]: %%timeit
...: sp.integrate.quad(lambda x: np.exp(-x**2), -np.inf, np.inf)[0]**2
570 µs ± 78.9 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
In [6]: sp.integrate.quad(lambda x: np.exp(-x**2), -np.inf, np.inf)[0]**2
Out[6]: 3.14159265358979271: I'm not sure how SymPy handles rounding, but it is typical to round the last digit so e.g. printing just 5 digits of 0.123456 would correctly be 0.12346
Same with sqrt(6)^2 - 6 - 0 on both. (-1)^5 works fine too.
So full marks for the ancient Casio and its emulator on the initial quality tests!
[1] https://www.calculator.org/calculators/Casio_fx-180P.html
[2] https://play.google.com/store/apps/details?id=uk.co.nickfine...
Note that [1] claims the calculator came out in 1985 but I'm sure I got mine in 1983 or 1984
Android's built in calculator on my phone and tablet (take note, Apple) both seem to get the answer right as well.
Fun problems for people trying to build their own calculators, but not really a problem if you're actually trying to solve equations using existing ones.
IF SQR(3 * 2) = SQR(3) * SQR(2) THEN…
evaluates to false, while, IF SQR(3 * 2) - SQR(3) * SQR(2) = 0 THEN…
evaluates to true. Similarly, A = SQR(3 * 2) - SQR(3) * SQR(2)
assigns zero (0.0) to variable A. So, SQR(3 * 2) <> SQR(3) * SQR(2)
but SQR(3 * 2) - SQR(3) * SQR(2) = 0
Which is kind of a nice paradox. (The same is true for a variety of other value combinations.) We learn, the value of an expression in a comparison is not the same as its result (probably due to result cleanup, especially, when the exponent happens to be zero.)[1] https://retrocomputingforum.com/t/investigating-basic-floati...
That happens all the time on traditional x86 - (non-SSE) floating point registers are 80 bits and then (usually) get rounded to 64 bits when you store them in memory, so you will very often have an expression that's nonzero if you use it immediately, but zero if you put it in a variable and use it later.
(The precision, BTW, is 4 bytes mantissa including a sign bit, and one byte exponent with a bias of 127. The floating point accumulators extract the sign to an extra byte, but there is no change in precision with regard to the storage format, other than said rounding bit. At least, this is what it's in Commodore BASIC, the flavor I'm most familiar with. Apple ][ BASIC should be pretty much the same, as it's closely related. And, notably, the 6502 processor only provides addition and subtraction for single-byte values, everything else has to be done in software. So there's no extra precision in any processor register, either.)
Also fun, Muller's recurrence: https://scipython.com/blog/mullers-recurrence/
I generally prefer to think of them as they are: able to represent a subset of the rationals, plus a couple other weird values, but not a field, and importantly, not obeying any of the usual algebraic laws with arithmetic.
Every floating point article (including IEEE 754) I've seen treats normal floating point numbers as dyadic rationals.
And at the same time, integer values (up until some decently high numbers) are represented correctly, so they would need a different range.
Floats are only ranges in that each real number (within the overall range limit) has a unique floating point representation to which it is closest, up to rounding at the precise interval boundary.
Floats can be extended to interval arithmetic, but doing so requires the extra work of keeping track of interval limits. When calculations lose precision (through cancellation of digits in the floating point representation), the interval of uncertainty is larger than the floating point precision.
This is a common confusion.
My point is that floating point numbers are real numbers, but the usual operations on them will not give you the same results as the operations on real numbers.
There's no randomness or fuzziness in floating-point rounding.
Until you deal with overflow.
And to be frank, this is just redefining "correctness". If you want to view FPs as real numbers (which they are), there's nothing wrong with pointing out that real number operations will give you incorrect results (even ignoring overflow).
> There's no randomness or fuzziness in floating-point rounding.
I did not imply there was.
https://www.google.com/search?tbo=p&tbm=bks&q=%22To+have+a+f...
$ echo "(2/3)*3-2" | bc -l
-.00000000000000000002You can try higher precision by setting the scale.
$ echo "scale = 100; (2/3)*3 - 2" | bc
-.000000000000000000000000000000000000000000000000000000000000000000\
0000000000000000000000000000000002 $ spigot '(2/3)*3-2'
0
furthermore, when you specify a precision number, this sets the precision of the final result, NOT the precision of intermediaries (which is what bc & dc, and many other arbitrary precision calculators do); every printed digit is correctly rounded. [edited to add: truncated towards zero by default; other rounding modes available as commandline flag] $ spigot -d72 'sin(1)-cos(1)'
0.301168678939756789251565714187322395890252640180448838002654454610810009
There are still conditions where spigot can't give an answer, such as $ spigot 'sin(asin(0.12345))'
.. spigot can never determine the next correctly rounded digit after "0.1234" with certainty, for reasons elucidated in the documentation.spigot is in debian and here's the author's page with links to a lot more background: https://www.chiark.greenend.org.uk/~sgtatham/spigot/
Because spigot rounds towards zero in that mode, and it can only bound the result as arbitrarily close to 0.12345 - i.e., it can never decide between 0.12344999..99something and 0.12345000..00something because the "something" part that might break the tie never appears. This is a general issue with exact real computation; in more complicated cases, it can even be undecidable whether some expression is exactly zero.
bc -l -e "(2/3) * 3 - 2"On the other hand, bash and zsh support this:
bc -l <<<'(2/3) * 3 - 2'
edit: neither does busybox. bc -v
bc 4.0.2
Copyright (c) 2018-2021 Gavin D. Howard and contributors
Report bugs at: https://git.yzena.com/gavin/bc$ echo "(2/3)*3-2" | bc -l > /dev/null && echo "0"
On my phone:
~ $ python -c "print( (2 / 3) * 3 - 2 )"
0.0
~ $ python --version
Python 3.11.5