# Formatting a number with fixed number of significant digits

I need a formatting function (say format[x_, n_]) which outputs numbers with fixed n significant digits without unnecessary dot. Examples:

format[.02,3] -> "0.0200"
format[.2,3] -> "0.200"
format[1,3] -> "1.00"
format[100,3] -> "100"
format[1000,3] -> "100\[Times]10^1"
format[10000,3] -> "100\[Times]10^2"


Any ideas? The shorter - the simpler!

-

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 -> "*"]


-

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):

The code of SciForm:

ClearAll[SciForm];
SyntaxInformation[SciForm]={"ArgumentsPattern"->{_,_.,OptionsPattern[]}};
SciForm::"sigz"= "In addition to 1 precise digit3 requested, 2 zero4 will appear as placeholder4.";
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]],{}];
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!

-
Maybe you will find interesting this related Q&A –  Kuba Aug 2 '13 at 12:00

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[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

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.

-
Thank you. But for some cases your function still gives wrong result: format[10, 3] gives 10 instead of 10.0. –  Alexey Popkov Aug 1 '12 at 12:19
Another case: format[.00000002, 3] should give "2.00\[Times]10^-8". –  Alexey Popkov Aug 1 '12 at 12:32
@AlexeyPopkov This case seems to work now after Update 2. –  Peter Breitfeld Aug 1 '12 at 13:28

in a similar case I used a combination of NumberForm and AccountingForm

ie:

AccountingForm[NumberForm[fn[i, j], {16, 16}]]

to get 16 digits

I get this:

0.0681758870688004