13
$\begingroup$

I want to show examples of round-off errors in some numerical algorithms to my student, in order to motivate the study of algorithms with a better behavior.

While it is easy in any other language, I found it horrendously complicated in Mathematica. Precision is tracked dynamically which makes SetPrecision[ ..., p] not useful to exhibit roundoff problems. I found out that ScientificForm (maybe with Round[] on top) could do the job, and I spent hours to try to get the output of ScientificForm in expression but failed so far...

I desperately want:

a=SetPrecision[5.291/0.003, 4]

to be strictly equal to 1764 not 1763.67`4 so that I don't get different results when I input:

 {SetPrecision[a*59.16, 4], SetPrecision[1764*59.16, 4]}
 (*  {1.043*10^5, 1.044*10^5}  *)

(this can be obtained by Round[a] but I want the same thing for small or large numbers where Round does not work)

Any simple method to achieve that?

Edit: After several clever answers (but maybe not simple enough) whom authors I am grateful to, due to the level of the students I think I am going to show the example myself with a projector to students and let them program easier stuffs. Thanks again.

$\endgroup$
  • 2
    $\begingroup$ Try setting the precision of the initial arguments: 5.291`4/0.003`4, which returns exactly 1764. $\endgroup$ – MarcoB Nov 15 '16 at 16:55
  • 3
    $\begingroup$ Maybe a duplicate of this: mathematica.stackexchange.com/q/130233/12 But that question's focus was on performance, and the answer reflects that. You want to do this for demonstration purposes. Take a look at the ComputerArithmetic package. It allows you to define an "arithmetic" with the given precision and behaviours and do calculations using it. It's slow but it's very flexible and excellent for demonstrations. You can even change the base of the representation and use base-10 instead of binary. $\endgroup$ – Szabolcs Nov 15 '16 at 16:58
  • $\begingroup$ @MarcoB Note that 5.2914/0.0034 // InputForm returns exactly 1763.6666666666666618178`3.6989700043360187, which displays as 1764.. Note the many extra guard digits and loss of precision. I don't think arbitrary-precision numbers will work as fixed-precision numbers. $\endgroup$ – Michael E2 Nov 16 '16 at 11:27
  • 2
    $\begingroup$ More generally, Mathematica is just too smart today with respect to numerics (and many other things). When I first had students learn math through Mathematica, it was easy to find examples of, say, a simple badly-conditioned linear system where roundoff error gave an absurdly wrong answer; a few years later, Mathematica could correctly treat such simple examples with aplomb, and it became much harder to find examples of where things went numerically awry. $\endgroup$ – murray Oct 2 '18 at 14:20
13
$\begingroup$

You can do

5.291`4/0.003`4
(* 1764. *)

Precision[%]
(* 3.69897 *)

But as you noted, the precision of the result is lower than 4 due to precision tracking.

Here's how to turn off precision tracking:


Since you want to do this for demonstration purposes, I suggest using the Computer Arithmetic package.

<<ComputerArithmetic`

We set the properties of the arithmetic. We use 4 digits and base 10.

SetArithmetic[4]
(* {4,10,RoundingRule->RoundToEven, ExponentRange->{-50,50}, MixedMode->False, IdealDivide->False, IdealDivision->False} *)

Then we can do calculations with "computer numbers":

ComputerNumber[5.291]/ComputerNumber[0.003]
(* 1763.000000000000000 *)

I don't really have any experience with this, but it looks like it was made just for what you need.

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ Thanks, the ComputerArithmetic package does the job..but I cannot reasonably ask students to type ComputerNumber[] anytime they input a number. This is not suitable for educational purpose $\endgroup$ – Xavier Nov 16 '16 at 4:28
  • 1
    $\begingroup$ @Xavier Why not? Do they really have to type explicit numbers that often instead of working with variables? You were talking about numerical algorithms. That sounds like working with variables after giving only a few input numbers. Also, it can be shortened: c=ComputerNumber; c@1.23. If you have to type a list, you can map: c /@ {1.23, 4.56, 7.89}. $\endgroup$ – Szabolcs Nov 16 '16 at 7:48
  • $\begingroup$ Thanks, I see. Well it's a little bit long to explain, students are beginner, time to teach is limited, the class is not an introduction to Mathematica...it is just to illustrate numerical algorithms.. $\endgroup$ – Xavier Nov 16 '16 at 9:48
6
$\begingroup$

Since you liked Szabolcs' answer, but don't want to type ComputerNumber when entering a number, you can hack the input to Mathematica using $PreRead to automatically interpret every number in the input as a ComputerNumber:

<<ComputerArithmetic`

ComputerArithmeticOn[] := (
  $PreRead = (
    # /. s_String /; SyntaxQ@s && Head@ToExpression@s === Real :> 
            RowBox[{"ComputerNumber", "[", s, "]"}]
    ) &;
)

ComputerArithmeticOff[] := ($PreRead = .;)

Now you can do

ComputerArithmeticOn[]

a = 5.291
(* 5.291000000000000000 *)

b = 0.003
(* 0.003000000000000000000 *)

a/b
(* 1763.000000000000000 *)

5.291/0.003
(* 1763.000000000000000 *)

ComputerArithmeticOff[]

5.291/0.003
(* 1763.67 *)

I restricted the input rewriting to just apply to explicit Real numbers to avoid breaking functions that require integers as input (like the SetArithmetic function), but this will probably still break plenty of stuff. It might cover what you need, though.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks very much, nice, this goes beyond my knowledge in Mathematica... but still not very handy for students, I mean you still cannot make operations with number in NumberForm and in ComputerArithmeticForm which for real beginners is hard to grasp, but appreciate your solution. $\endgroup$ – Xavier Nov 16 '16 at 9:52
2
$\begingroup$

Round takes a second argument that specifies what to round to. So, I think you can achieve what you want with:

roundToPrecision[x_, prec_] := SetPrecision[
    Round[x, 10^-Floor[prec - RealExponent[x]]],
    prec
]

For your example:

a = roundToPrecision[5.291/0.003, 4];
a //InputForm

1764.`4.

Then, your second example gives:

{SetPrecision[a*59.16,4], SetPrecision[1764*59.16,4]} //InputForm

{104358.2399999999906867743`4., 104358.2399999999906867743`4.}

| improve this answer | |
$\endgroup$
  • $\begingroup$ Of course, if wanted, one could put roundToPrecision[] in $Pre. $\endgroup$ – J. M.'s discontentment Oct 2 '18 at 5:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.