8
$\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$
7
$\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$
  • $\begingroup$ Nice - you'd think this would be native by now though. $\endgroup$ – alancalvitti Dec 1 '16 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$ – Quantum_Oli Dec 1 '16 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$ – Quantum_Oli Dec 9 '16 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$
  • $\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$ – Quantum_Oli Nov 30 '16 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 '16 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$
  • $\begingroup$ Please see my updated answer. I borrowed your use of ExponentFunction to replace the poorly chosen InputForm. $\endgroup$ – Mr.Wizard Dec 2 '16 at 22:57

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.