YouMost users probably want to use SetPrecision
, which preserves extra digits and automagically handles fractional digits of precision. For example: 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.
xFormat[sigFigNumber[s_, d_]] := N[Pi]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_, 3.14159d_] *)
x:= //
PrecisionsigFigNumber[
(* MachinePrecisionIf[d *)
y== =\[Infinity], SetPrecision[xs, 3]Round[s, 10^(*-d 3.14+ *Floor[Log10[Abs[s]]] + 1)]], d]
y
createSigFigNumber[s_] //:= Precision
(*createSigFigNumber[SetPrecision[s, 3.\[Infinity]], *)
z = SetPrecision[PiFloor[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, 5]3] (* 31.141623, sigFigNumber[123/100, 3] *)
zcreateSigFigNumber[1.23`3] // Precision (* 51.23, sigFigNumber[123/100, 3] *)
v = ycreateSigFigNumber[1] * z (* 9.87 *)
v // Precision (* 2.99581, sigFigNumber[1, Infinity] *)
wcreateSigFigNumber[Pi] = y + z (* 6.28 (*)
w // Precision\[Pi], (*sigFigNumber[Pi, 3.29671Infinity] *)
So weWe can see that the significand of SetPrecisionsigFigNumber
sets the number of significant digitsis stored in exact form, rounded to the desired number. This precision will be used when printing to setcorrect decimal place, but is displayed as a decimal (also with the correct number of digitsdecimal places showing). Also, and will also be used when computing with other arbitrary-precisionexact numbers to set thehave a precision of the resultInfinity
and so are left unrounded.
However, mixing machine-precision and arbitrary-precision numbers does not work, as the result will be converted backNow let's define some functions to machine precision:
Precision[x*y] (* MachinePrecision *)
(This does not affect using arbitrary-precision numbersdo math with exactthese numbers like 4
, 134/267
,. I'll just give the examples of multiplication Pi
, or(easy) and addition E
.(harder) We can get around this restriction by converting any machine-precision numbers to exact form, or setting their precision manually:.
Precision[Rationalize[xsigFigNumber[s1_, 0]*y]d1_] (* 3.sigFigNumber[s2_, *)d2_] ^:=
Precision[SetPrecision[x createSigFigNumber[s1 s2, $MachinePrecision]*y]Min[d1, (*d2]]
sigFigNumber[s1_, 3.d1_] *)+ sigFigNumber[s2_, d2_] ^:=
createSigFigNumber[s1 + s2,
Floor[Log10[Abs[s1 + s2]]] -
Max[Floor[Log10[Abs[s1]]] - d1, Floor[Log10[Abs[s2]]] - d2]]
NoteFirst note that you must use $MachinePrecision
, notI am using MachinePrecision
. The first one will evaluateupvalues to a number (15.9546
define the behavior of operators on my 64-bit machinethis new object.
For multiplication the problem is simple, equal to 53 bits: the sizeresulting number of significant figures is simply the significandminimum of an IEEE double) and return an arbitrary-precision number with precision equal to a machine-precision number, while the second onetwo input numbers. For addition the problem is more complex, requiring us to first convert the symbol that Mathematica usesprecisions to represent machine-precision numbersaccuracies, sofind the number will stay machinelargest (highest-precisionvalue last decimal place) and then finally convert back to precision.
Note that Mathematica keeps more digits internally than are displayed, Now for examplesome examples:
yx = createSigFigNumber[1.23, 3] (* 31.1423 *)
y //= FullFormcreateSigFigNumber[0.45, 2] (* 3.14159265358979311599796346854418516159`30.45 *)
This is a good thing. It prevents rounding errors, so that you know your 3-digit-precision number actually is accurate to three digits. For a simple example, convert the number $1.004$ to three digit precision, and double it. Doing this naively you get $1.00\times 2=2.00$. However, the answer should be $2.01$, closer to the actual full-precision value of $2.008$.
However, if you want to retain this erroneous behavior you will have to use a special function to round the numbers. Try this one:
round[n_]z := SetPrecision[Round[ncreateSigFigNumber[0.6, 10^Floor[-Precision[n]1] +(* Log10[Abs[n]]]],0.6 *)
x Precision[n]]y (* 0.55 *)
y z (* 0.3 *)
z x (* 0.147 *)
round[y]x + y (* 31.1468 *)
%y //+ FullFormz (* 31.142`30 *)
z + x (* 1.8 *)
Note that in this case it gives you one extra digit. This is due to the definition of precision in Mathematica:
With absolute uncertainty
dx
,Precision[x]
is-Log[10,dx/x]
.
This is incompatible with the typical definition of precisionlooks pretty good, which has discontinuous behavioralthough I see one possible problem: you might expect y + z
to return 1.1
(ethe unrounded value is 1.05
).g 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 $1.00$10.5
, and the nearest $9.99$ have 3 digits of precisioneven integer is 10
, butnot $10.00$ has 4)11
, resulting in 1.0
instead of 1.1
.
To So if your students use round-towards-even the typical definition you'llresults will match. Otherwise you will have to round manuallyre-write the Round
function to something like this:
round[n_sigFigRound[a_, d_]b_] :=
SetPrecision[Round[n,b 10^(Ceiling[Log10[Abs[n]]] -IntegerPart[a/b] d)],+ d]
a = round[1, 3] (* 1.00 *)
a /If[Abs[FractionalPart[a/ FullFormb]] (*>= 1.`3. *)
b = round[0.999/2, 3] (* 0.999Sign[a], *0])
b /
sigFigRound[105/100, FullForm1/10] (* 0.999`3.11/10 *)
To avoid Mathematica's default precision-handling behavior you will alsoYou'll have to compute the precision and re-round for every operation, or at least write custom functions forto handle every operation.
This is not a good idea. You will then be subject to errors as I describe above However, and the whole point of Mathematica is that you can ignore details like thishave a choice. In summary, just useorder to handle division/subtraction you can either write functions for SetPrecisionDivide
and your calculations will automagically work out as accurately as theySubtract
, or you can write functions for Times
and Power
. See what Mathematica does internally:
a - b // FullForm (* Plus[a, Times[-1, b]] *)