11
$\begingroup$

I'm having a surprising amount of difficulty in trying to format some numbers with errors. I can't find any previous attempts to do this here or elsewhere and it would surely be of use to the community.

I have some number-error pairs: {12345.6789, 0.0012345} which I would like to convert to strings with both the number and the error rounded to the same number of decimal places, such that the error has a precision of two significant figures.

f[{12345.6789, 0.03456}]
f[{12345.6789, 3.456}]
f[{12345.6789, 345}]

"12345.679 ± 0.035"

"12345.7 ± 3.5"

"12340 ± 350"

I'm struggling to get this done with RealDigits, N, NumberForm, StringRiffle and others.

Can anyone produce a robust working implementation of f?

Edit: Additional test cases

There are quite a few subtly different behaviours when handling different number, including rounding, adding trailing zeros, handling precision = 0, and probably others. Therefore for the sake of comprehensively testing implementations here are a list of inputs and the required outputs (if you can think of any additional useful test cases then let me know / add them):

f[{12.3456, 0.0123}]     ->     "12.346 ± 0.012"
f[{12.3456, 0.123}]      ->     "12.35 ± 0.12"
f[{129.3456, 1.23}]      ->     "129.3 ± 1.2"
f[{-129.3456, 12.3}]     ->     "-129. ± 12."
f[{12.9999, 0.02}]       ->     "13.000 ± 0.020"
f[{1, 100}]              ->     "0 ± 100."
f[{-1, 100}]             ->     "0 ± 100."
f[{12345.6789, 345}]     ->     "12350. ± 340."
f[{12345.6789, 3456}]    ->     "12300. ± 3500."
$\endgroup$
2
  • $\begingroup$ It seems this is a duplicate: Expression of uncertainty in measurement. but your answer here is far superior so I'd mark the old one as a duplicate. Do you agree? $\endgroup$
    – Kuba
    Dec 1, 2016 at 8:55
  • 1
    $\begingroup$ at 12.0 ,just use Around $\endgroup$ Jan 22, 2020 at 4:17

4 Answers 4

8
$\begingroup$

Update

This seems to do what you want a bit more simply than your own solution.

f[{a_, b_}] :=
  NumberForm[
    SetAccuracy[a ± b, Accuracy @ SetPrecision[b, 2]],
    ExponentFunction -> (Null &)
  ] // ToString // Quiet

Your test:

f[{12.3456, 0.0123}]
f[{12.3456, 0.123}]
f[{129.3456, 1.23}]
f[{-129.3456, 12.3}]
f[{12.9999, 0.02}]
f[{1, 100}]
f[{-1, 100}]
f[{12345.6789, 345}]
f[{12345.6789, 3456}]

"12.346 ± 0.012"

"12.35 ± 0.12"

"129.3 ± 1.2"

"-129. ± 12."

"13.000 ± 0.020"

"0. ± 100."

"0. ± 100."

"12350. ± 340."

"12300. ± 3500."

$\endgroup$
3
  • $\begingroup$ Nice - you'd think this would be native by now though. $\endgroup$ Dec 1, 2016 at 1:19
  • $\begingroup$ Thanks, that seems to get most of the way there. In a couple of cases the output is not quite as I would want, for example f[{12.6789, 0.00123}] -> "12.6789 \[PlusMinus] 0.0012000000000000001" and although the answer for f[{12.9999, 0.02}] is correct ("13. \[PlusMinus] 0.02"), I'd like to include the relevant trailing zeros, eg "13.000 \[PlusMinus] 0.020". I'll have a proper look with this and @corey979's code later and try to form a fully robust solution, there are a lot of edge cases! Also the 345 being rounded to 340 I'm ok with, that's the normal banker's rounding of Round. $\endgroup$ Dec 1, 2016 at 7:53
  • 1
    $\begingroup$ Thanks Mr.W, this works nicely. The only time I've spotted it fail is with an input along the lines of {0,0.01} which produces an output " -4\n0. \[Times] 10 \[PlusMinus] 0.010", however this is due to a failing in NumberForm ask discussed and circumvented here: mathematica.stackexchange.com/q/133065/6588 $\endgroup$ Dec 9, 2016 at 11:29
6
$\begingroup$
f[{a_, b_}, prec_] := 
 Block[{x = {a, b}, y}, y = SetAccuracy[x, prec]; 
  ToString @ y[[1]] <> " ± " <> ToString @ y[[2]]]

f[{12345.6789, 0.0012345}, 4]

"12345.679 ± 0.001"

f[{12345.6789, 3.456}, 2]

"12345.7 ± 3.5"

f[{12345.6789, 345}, 1]

"12346. ± 345."

f[Round[#, 5]& @ {12345.6789, 345}, 1]

"12345. ± 345."

$\endgroup$
2
  • $\begingroup$ I hadn't come across SetAccuracy before. I would want trailing zeros where needed, for example f[{12345.6789, 0.0012345}, 4] should be "12345.6790 ± 0.0010". Also it struggles with some precisions, eg: f[{12345.6789, 12.345}, 0] comes out at a mess. $\endgroup$ Nov 30, 2016 at 19:36
  • $\begingroup$ Then do f[Round[#, 0.001] &@{12345.6789, 0.0012345}, 5] - yields "12345.6790 ± 0.0010". Rounding is one thing that can be done on the input itself; I focused on adressing the issue of displaying given number of digits. Also, SetAccuracy is probably designed differently than what you think. $\endgroup$
    – corey979
    Nov 30, 2016 at 19:44
4
$\begingroup$

Building on @Mr.Wizard♦ 's answer I have come up with the following. I use SetPrecision to set the significant figures of the error to two (ala Mr.W). I then use RealDigits, which tells me about the number of significant figures either side of the decimal place, to calculate the necessary precision for the number, a. I can the use SetPrecision again, instead of Round, but I have to catch, using If, the cases where the required precision is $\le 0$.

I avoid using InputForm as I think this is what leads to the 0.0012000000000000001 problem. But I do use NumberForm to convert 1.*10^2 to 100.. The Quiet is there to turn off the warning about the presence of trailing zeros - which I actually want!

My function:

f[{a_, b_}] := Module[{aa, bb, p},
  bb = SetPrecision[b, 2];
  p = Last[RealDigits[a]] - Last[RealDigits[bb]] + 2;
  aa = If[p > 0, SetPrecision[a, p], 0];
  Quiet[
   ToString@
    NumberForm[PlusMinus @@ {aa, bb}, ExponentFunction -> (Null &)],
   {NumberForm::sigz}
   ]
  ]

This passes all the test cases I have outlined so far above, please let me know if you find any tests you think it fails. And obviously if you find a more elegant implementation!

f[{12.3456, 0.0123}]
f[{12.3456, 0.123}]
f[{129.3456, 1.23}]
f[{-129.3456, 12.3}]
f[{12.9999, 0.02}]
f[{1, 100}]
f[{-1, 100}]
f[{12345.6789, 345}]
f[{12345.6789, 3456}]

"12.346 ± 0.012"

"12.35 ± 0.12"

"129.3 ± 1.2"

"-129. ± 12."

"13.000 ± 0.020"

"0 ± 100."

"0 ± 100."

"12350. ± 340."

"12300. ± 3500."

$\endgroup$
1
  • $\begingroup$ Please see my updated answer. I borrowed your use of ExponentFunction to replace the poorly chosen InputForm. $\endgroup$
    – Mr.Wizard
    Dec 2, 2016 at 22:57
3
$\begingroup$

This extends Mr. Wizard's answer to handle large/small numbers:

printNumberError[a_, b_] := 
 Block[{exponent = Floor[Log[10, Abs[a]]], 
    sas = SetAccuracy[#1, Accuracy[SetPrecision[#2, 2]]] &}, 
   If[exponent < 6 && exponent > -4, 
    NumberForm[sas[Row[{a, "±", b}, " "], b], 
     ExponentFunction -> (Null &)], 
    Block[{aa = a/10^exponent, bb = b/10^exponent}, 
     Row[{"(", sas[aa, bb], "±", sas[bb, bb], ")", 
       "×", Superscript[10, exponent]}, " "]]]];

For example:

printNumberError[-6.390809*10^-6, 3.4485*10^-8]

Gives:

$$(-6.391\pm0.034)\times10^{-6}$$

$\endgroup$
5
  • $\begingroup$ Good addition; thanks. I suggest making the SetAccuracy[a, Accuracy[SetPrecision[b, 2]]] operation a function to reduce repetition in this code. I can edit that in if you like. $\endgroup$
    – Mr.Wizard
    Jan 22, 2020 at 15:51
  • $\begingroup$ Good point ... updated my code. $\endgroup$ Jan 24, 2020 at 6:09
  • $\begingroup$ @Mr.Wizard ... Also, I generally keep my Mma code in plain text (*.m) files so that I can use source control in a meaningful way. Thus, special characters like $\pm$ are problematic. $\endgroup$ Jan 24, 2020 at 6:17
  • $\begingroup$ There is a difference between "private" Mathematica characters and standard characters that Mathematica uses. ± is part of the latter and it should work fine in plain text. Have you actually experienced problems or did you assume it would not work? $\endgroup$
    – Mr.Wizard
    Jan 24, 2020 at 9:37
  • 1
    $\begingroup$ I did a test and git (on Ubuntu) does handle both "±" and "×" without issue. $\endgroup$ Jan 25, 2020 at 17:32

Your Answer

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

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