Formatting a number with fixed number of significant digits

You probably need to read tutorial Output Formats for Numbers, but long story short, you are looking for NumberForm

NumberForm[N[Pi], 10]

3.141592654

Main thing is controlling number of precise digits and the number of digits to the right of the decimal:

NumberForm[1.23456, {3, 4}]

1.2300

Cool things you can also do:

NumberForm[30!, DigitBlock -> 5, NumberSeparator -> " "]

265 25285 98121 91058 63630 84800 00000

NumberForm[1234567890., 10, ExponentStep -> 6, NumberMultiplier -> "*"]

enter image description here


Here I present the scientific formatting function I have developed. It allows to restrict the displayed digits to only precise digits (negative and zero precision are also supported) and to specify the maximum number of zeros after the decimal point before converting the number to exponential form (negative values of this option are interpreted as the minimum number of digits before decimal point which will be showed). The option PadSign controls sign padding for positive numbers. The option Align allows to specify where the \[AlignmentMarker] should be placed inside of the number: before decimal point ("."), before first significant digit of the number ("FirstDigit") or it should not be included in the output (any other value). The output is always wrapped in Item and the option Alignment->"\[AlignmentMarker]" is set when the Align is not switched off. It is also possible to pass any options of Item through SciForm.

Here is a demonstration of what is possible with SciForm (the code for generating this table see at bottom of this post): table1

The code of SciForm:

ClearAll[SciForm];
Options[SciForm]=Join[Options[Item],{Align->"FirstDigit",PadSign->False}];
SyntaxInformation[SciForm]={"ArgumentsPattern"->{_,_.,OptionsPattern[]}};
SciForm::"sigz"= "In addition to `1` precise digit`3` requested, `2` zero`4` will appear as placeholder`4`.";
SciForm[x_?NumericQ,prec_Integer,thr_Integer:4,opts:OptionsPattern[]]:=
   Module[{rd,e=MantissaExponent[x][[2]],m1,m2,sign,mess,exp,i,al,alF},
      rd=If[prec>0,ToString/@RealDigits[Round[x,10^(-prec+e)],10,prec][[1]],{}];
      sign=If[Negative[x],"-",If[TrueQ@OptionValue[PadSign],ToString[Invisible["+"],StandardForm],""]];
      If[Head[alF=al=OptionValue[Align]]===List,al=First@alF;alF={"\[AlignmentMarker]",alF[[2]]},alF={"\[AlignmentMarker]",Center}];
      m1=If[TrueQ[al==="FirstDigit"],"\[AlignmentMarker]",""];
      m2=If[TrueQ[al==="."],"\[AlignmentMarker]",""];
      i=If[TrueQ[MatchQ[al,"."|"FirstDigit"]],
             Item[#,Alignment->alF,FilterRules[{opts},Options[Item]]]&,Item[#,FilterRules[{opts},Options[Item]]]&];
      exp[n_]:=ToString[Superscript["10",n],StandardForm];
      mess:=Message[SciForm::"sigz",prec,-thr-prec,If[prec>1,"s",""],If[-thr-prec>1,"s",""]];
      i@Which[
         e!=prec<=0,StringJoin[sign,m1,"0",m2,".?*",exp[e-prec]],
         e==prec<=0,StringJoin[sign,m1,"0",m2,".?"],
         0==e>=-thr,StringJoin[sign,"0",m2,".",m1,rd],
         0>e>=-thr,StringJoin[sign,"0",m2,".",Array["0"&,-e],m1,rd],
         0>=-thr>e,StringJoin[sign,"0",m2,".",m1,rd,"*",exp[e]],
         1<=-thr>e&&-thr<prec>=1,StringJoin[sign,m1,Insert[rd,{m2,"."},1-thr],"*",exp[e+thr]],
         1<=-thr==prec>e,StringJoin[sign,m1,rd,m2,"*",exp[e+thr]],
         1<=-thr>prec>e,mess;StringJoin[sign,m1,rd,Array["0"&,-thr-prec],m2,"*",exp[e+thr]],
         prec>e>0,StringJoin[sign,m1,Insert[rd,{m2,"."},e+1]],
         prec==e>0&&-thr<=prec,StringJoin[sign,m1,rd,m2],
         e==-thr>prec>0,mess;StringJoin[sign,m1,rd,Array["0"&,-thr-prec],m2],
         e>prec>0&&-thr<=prec,StringJoin[sign,m1,rd,m2,"*",exp[e-prec]],
         e>=prec>0&&-thr>prec,mess;StringJoin[sign,m1,rd,Array["0"&,-thr-prec],m2,"*",exp[e+thr]]
]];

The code for the above table:

data = {.0000000123456789, .000000123456789, -.00000123456789, \
-.0000123456789, .000123456789, .00123456789, .0123456789, .123456789,
    1.23456789, 12.3456789, 123.456789, 1234.56789, 12345.6789, 
   123456.789};
(*Note that NumberForm does not print Message although when called as \
NumberForm[123456.789,5] it does. Is it a bug? *)
Grid[Prepend[
  {SciForm[#, 4
      , Align -> "."
      , BaseStyle ->
       FontFamily -> "Times"],
     SciForm[#, 4, 5
      , BaseStyle ->
       FontFamily -> "Times"],
     SciForm[#, 4, -4
      , BaseStyle ->
       FontFamily -> "Times"],
     SciForm[#, 4, -1
      , BaseStyle ->
       FontFamily -> "Times"]} & /@ data,
  Item[#, Alignment -> "\[AlignmentMarker]", 
     BaseStyle -> {10, Bold}] & /@ {
    "SciForm[\[AlignmentMarker]#,4\[IndentingNewLine],Align\[Rule]\".\
\"\[IndentingNewLine],BaseStyle\[Rule]\[IndentingNewLine]FontFamily\
\[Rule]\"Times\"]&",
    "SciForm[#\[AlignmentMarker],4,5\[IndentingNewLine],Align\[Rule]\"\
FirstDigit\"\[IndentingNewLine],BaseStyle\[Rule]\[IndentingNewLine]\
FontFamily\[Rule]\"Times\"]&",
    "SciFor\[AlignmentMarker]m[#,4,-4\[IndentingNewLine],Align\[Rule]\
\"FirstDigit\"\[IndentingNewLine],BaseStyle\[Rule]\[IndentingNewLine]\
FontFamily\[Rule]\"Times\"]&",
    "SciFor\[AlignmentMarker]m[#,4,-1\[IndentingNewLine],Align\[Rule]\
\"FirstDigit\"\[IndentingNewLine],BaseStyle\[Rule]\[IndentingNewLine]\
FontFamily\[Rule]\"Times\"]&"}
  ], Frame -> All, Alignment -> {Automatic, Center}]

Any ideas and suggestions are welcome!


It's not so easy to achieve your goal, because NumberForm always wants a number n of digits to precision or a pair {totaldigits, digits-after-decimal-point}. So we must use PaddingForm or Row to do it. The solution isn't very short then. But the following code seems to work:

Clear[format]
Options[format] = Options[NumberForm];
format[x_, sd_] := 
 Module[{m, e, man, exp, num, n, f}, {m, e} = MantissaExponent[x];
  If[Abs[x] <= 10^-sd,
   Row[{PaddedForm[10 m, {sd, sd - 1}], Superscript["\[Times]10", e - sd + 2]}],
   If[e < sd,
    n = sd - e - 1; f = sd - e;
    If[n == 0, PaddedForm[x, {sd, f}], PaddedForm[x, {n, f}]], 
    If[sd == e, PaddedForm[x, sd],(*else sd<e*)
     Row[{Round[10^sd m, 10^-sd], 
       Superscript["\[Times]10", e - sd]}]]]]]    

For your examples you will get

Mathematica graphics

Update
Replacing the Numberform, in If[n==0,...] with PaddedForm[x,{sd,f}] seems to work for the cases, which the old routine failed, as Alexey Popkov mentioned in his comment.

Update 2

Seems to work now for numbers smaller than 10^-sd.