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$
4
  • 2
    $\begingroup$ Try setting the precision of the initial arguments: 5.291`4/0.003`4, which returns exactly 1764. $\endgroup$
    – MarcoB
    Commented Nov 15, 2016 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
    Commented Nov 15, 2016 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
    Commented Nov 16, 2016 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
    Commented Oct 2, 2018 at 14:20

3 Answers 3

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.

$\endgroup$
3
  • 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
    Commented Nov 16, 2016 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; [email protected]. If you have to type a list, you can map: c /@ {1.23, 4.56, 7.89}. $\endgroup$
    – Szabolcs
    Commented Nov 16, 2016 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
    Commented Nov 16, 2016 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.

$\endgroup$
1
  • $\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
    Commented Nov 16, 2016 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.}

$\endgroup$
1
  • $\begingroup$ Of course, if wanted, one could put roundToPrecision[] in $Pre. $\endgroup$ Commented Oct 2, 2018 at 5:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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