[H]ow do I fix it once and for all for the entire Notebook?
I suppose it depends on how you want to treat results that contain round-off error. You cannot really get rid of the problem, only shift where the problem arises. For instance, in IntegerPart[x], IntegerPart (nor usually the program/programmer) knows at that point whether the roundoff error that led to x is positive, negative, or zero. If you fix IntegerPart[x] when x comes from a/a and is one bit less than 1., then it will be broken when another x has a roundoff error that increases it to one bit less than 1.. One may be willing to live with it being broken in this way. Here's a way based on Equal comparing with tolerance:
intPart // ClearAll;
intPart[x_?NumericQ] /; x == Round[x] := Round[x];
intPart[x_?NumericQ] := IntegerPart[x];
intPart[a/a]
(* 1 *)
Make it Listable if your code relies on IntegerPart being listable. You can change the tolerance used by Equal with Internal`$EqualTolerance.
Another approach is to "correct" real numbers that are close to integers:
snapToInteger // ClearAll;
snapToInteger[expr_] :=
expr /. {x_Real /; x == Round[x] :>
SetPrecision[Round[x], Precision[x]]};
IntegerPart[snapToInteger[a/a]]
(* 1 *)
Either approach is likely to incur a performance hit, which may or may not be important.
For what it's worth, this works on x = a/a, because of the nature of the rounding error and how MatchQ works on machine reals and bignums, and it's a bit faster:
intPart // ClearAll;
intPart[x_Real] /; MatchQ[x, N[Round[x], Precision[x]] :=
Round[x];
intPart[x_?NumericQ] := IntegerPart[x];
arr = RandomReal[1, 100000]; Tally[RealDigits /@ (arr/arr)] => {{{{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, 0}, 15338}, {{{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1}, 84662}}. ~15% numbers giving not 1 when you try to divide number on itself
– Kirill Belov Aug 08 '22 at 14:38IntegerPart[Divide[a, a]]. It has been noted before on this site that "a / a" is parsed asTimes[a, Power[a, -1]]. The roundoff error in1 / a = 99999.99999999999meansa * (1/a)does not cancel (and sometimes doesn't for other values ofa). – Michael E2 Aug 08 '22 at 14:42Table[a = 10.`16^-k; IntegerPart[a/a], {k, 100}]. The use of arbitrary-precision means more bits than in machine precision are used to represent the floating-point numbers (plus precision tracking, which is unimportant here). All the extra precision does is change where the problem occurs. – Michael E2 Aug 08 '22 at 14:58