Most users probably want to use SetPrecision, which preserves extra digits and automagically handles fractional digits of precision. However, in this case, we need to somehow override this behavior.
I'll use a custom object, sigFigNumber. First I'll define how it's displayed.
Format[sigFigNumber[s_, d_]] := N[s, d]
So we can see that sigFigNumber has two fields: the first one is the significand, and the second is the desired number of digits of precision. But we need a way to get this object!
createSigFigNumber[s_, d_] :=
sigFigNumber[
If[d == \[Infinity], s, Round[s, 10^(-d + Floor[Log10[Abs[s]]] + 1)]], d]
createSigFigNumber[s_] :=
createSigFigNumber[SetPrecision[s, \[Infinity]],
Floor[Log10[Abs[s]]] - Floor[-Precision[s] + Log10[Abs[s]]]]
We can use the two-argument form of createSigFigNumber to round a number to the specified number of digits. If we omit the number of digits, then the second function is invoked, which automatically determines the number of digits from the precision of the number. Let's look at some examples now.
createSigFigNumber[1.234, 3] (* 1.23, sigFigNumber[123/100, 3] *)
createSigFigNumber[1.23`3] (* 1.23, sigFigNumber[123/100, 3] *)
createSigFigNumber[1] (* 1, sigFigNumber[1, Infinity] *)
createSigFigNumber[Pi] (* \[Pi], sigFigNumber[Pi, Infinity] *)
We can see that the significand of sigFigNumber is stored in exact form, rounded to the correct decimal place, but is displayed as a decimal (also with the correct number of decimal places showing). Also, exact numbers have a precision of Infinity and so are left unrounded.
Now let's define some functions to do math with these numbers. I'll just give the examples of multiplication (easy) and addition (harder).
sigFigNumber[s1_, d1_] * sigFigNumber[s2_, d2_] ^:=
createSigFigNumber[s1 s2, Min[d1, d2]]
sigFigNumber[s1_, d1_] + sigFigNumber[s2_, d2_] ^:=
createSigFigNumber[s1 + s2,
Floor[Log10[Abs[s1 + s2]]] -
Max[Floor[Log10[Abs[s1]]] - d1, Floor[Log10[Abs[s2]]] - d2]]
First note that I am using upvalues to define the behavior of operators on this new object.
For multiplication the problem is simple, the resulting number of significant figures is simply the minimum of the two input numbers. For addition the problem is more complex, requiring us to first convert the precisions to accuracies, find the largest (highest-value last decimal place) and then finally convert back to precision. Now for some examples:
x = createSigFigNumber[1.23, 3] (* 1.23 *)
y = createSigFigNumber[0.45, 2] (* 0.45 *)
z = createSigFigNumber[0.6, 1] (* 0.6 *)
x y (* 0.55 *)
y z (* 0.3 *)
z x (* 0.7 *)
x + y (* 1.68 *)
y + z (* 1.0 *)
z + x (* 1.8 *)
This looks pretty good, although I see one possible problem: you might expect y + z to return 1.1 (the unrounded value is 1.05). The Mathematica documentation states that "Round rounds numbers of the form x.5 toward the nearest even integer." In this case, it's effectively rounding 10.5, and the nearest even integer is 10, not 11, resulting in 1.0 instead of 1.1. So if your students use round-towards-even the results will match. Otherwise you will have to re-write the Round function to something like this:
sigFigRound[a_, b_] :=
b (IntegerPart[a/b] +
If[Abs[FractionalPart[a/b]] >= 1/2, Sign[a], 0])
sigFigRound[105/100, 1/10] (* 11/10 *)
You'll have to write functions to handle every operation. However, you have a choice. In order to handle division/subtraction you can either write functions for Divide and Subtract, or you can write functions for Times and Power. See what Mathematica does internally:
a - b // FullForm (* Plus[a, Times[-1, b]] *)
N[#,3]&? http://reference.wolfram.com/language/ref/N.html?q=N – Cameron Murray Jan 25 '15 at 04:52NumberForm? – m_goldberg Jan 25 '15 at 05:25@DavidG.Stork I have been searching but have not found it documented, only people trying to work around it, not blatantly stating that "this is a perennial annoyance with Mathematica". Thanks for letting me know of the persisting issue though.
@MikeHoneychurch Yes I know the difference, and that is why the use of
Nis useless.@m_goldberg yes I have, but the output is not something that can be worked with mathematically so that's also useless for computational purposes.
– George Papadopoulos Jan 25 '15 at 05:51