Skip to main content

functions - Permanently rounding numbers/values to a given number of significant figures


I am searching for a simple way of rounding and setting an input to a desired number of significant figures. There are a vast number of posts that discuss similar problems, but none address this issue. I turned to writing my own function. Here it is below:


SigFig[x_, n_] :=
(p := Solve[10^i <= Abs[x] < 10^(i + 1), i, Integers][[1, 1, 2]];
N[Round[x, 10^(p - n + 1)], n]) // Quiet

SigFig rounds any real number x (of magnitude greater than or equal to 1) to a specified number of significant figures n.



Obviously the biggest flaw in this function is not catering for numbers of magnitude less than 1, and thus also the problem of preceeding insignifcant zeroes such as 0.00123. Admittedly it works for my specific application as all my numbers are between 1 and 10000; so honestly I can't be bothered trying to make it universal due to time constraints.


So, is there a simple pre-defined function or simple way of doing such a simple task that I have completely missed? I ultimately would like to know of a simple significant figure rounding function that works all the time and is much less complicated than what I've given here. I believe that if no such solution exists, then (1) a solution for this task needs to be documented so that people can search for it and (2) the shortcoming in lack of said function needs to be addressed to Wolfram Research. Does anyone have any suggestions on a pre-defined, pre-established or a custom function of their own that improves upon my current suggestion?



Answer



Please tell me if this simplified function does what you want:


f[x_, n_] := Round[x, 10^(1 - n + ⌊ Log10 @ Abs @ x ⌋)] ~SetPrecision~ n

Test:


Table[f[x*Pi, 4], {x, {1/100, 1/10, 1, 10, 100}}]

% // FullForm



{0.03142, 0.3142, 3.142, 31.42, 314.2}

List[0.03142`4., 0.3142`4., 3.142`4., 31.42`4., 314.2`4.]



Update


The OP wrote:




I understand that there is a difference in the 'implied precision' between the number 0.5 and 1/2 when entered in Mathematica. But my request is to perform a very simple calculation: consider the number 1.004 and double it. The answer is 2.008. Then round it to 3 sig. fig, the answer is 2.01. Take that number, divide it by two/multiply by half/multiply by 0.5 (mathematically equivalent). The mathematical answer is 1.005. I did not ask to round the final answer to 3 sig. fig. as that could be done by doing f to the final answer. Is this possible?



I suspect that I am failing to comprehend the needs that are behind this request and as such that my recommendations may be inadequate or inappropriate. However I am trying both to understand and to help, so I shall venture forward.


When performing the following operations:


1.004*2
f[%, 3]
x = %/2


2.008


2.01

1.01

The result is as desired except in the output formatting; the underlying value of x is correct as can be seen with FullForm:


FullForm[x]


1.005`3.


Increasing its precision also results in all four digits being formatted in output:


SetPrecision[x, 4]


1.005

If this is not an acceptable method then perhaps setting a higher precision beforehand would be usable.


1.004*2
f[%, 3]

f[%, 4]
%/2


2.008

2.01

2.010


1.005

If this too is not acceptable then to the best of my knowledge Mathematica has no floating point format that is, as you seem to want a fundamentally different precision arithmetic than what is implemented in Mathematica.


Perhaps working with Rational values could work for you. As a rough and partial example:


SetAttributes[num, NHoldAll]

num /: num[x_] * (num[y_] | y_.) := num[x * y]
num /: num[x_] + (num[y_] | y_.) := num[x + y]

Format[num[x_]] := N[x]


g[num[x_] | x_, n_] := num @ Round[x, 10^(1 - n + ⌊Log10@Abs@x⌋)]

Now:


g[1.00412, 4]  (* step to show that g may be used more than once *)

%*2

g[%, 3]


%/2


1.004

2.008

2.01

1.005




Related Q&A's:



Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...

Is there a way to do conditional matrix loop using 'continue'

I have the following: n = 3; m = 5; ww = RandomReal[{0, 0.1}, {n, n}]; uu = RandomReal[{0, 1}, {m, n}]; pp = RandomReal[{0, 1}, {n, n}]; ss = RandomInteger[{0, 5}, {m, n}]; Grid[{{"ww", "uu", "pp", "ss"}, {ww // TableForm, uu // TableForm, pp // TableForm, ss // TableForm}}, Spacings -> {5, 2}, Dividers -> All] where I would like to look at every element of matrix ss and produce a matrix tt , with zeroes at the locations in ss which have zeroes, and in all other positions do the following: tt = (-1/Subscript[ww, m]) Log[(1 - uu)/(Subscript[pp, m - 1])], where Subscript[ww, m] is the value at index of ww matrix and where Subscript[pp, m - 1] is the value at index-1 of pp matrix. So for example if the first value ever read from matrix ss happens to be 2, then value taken from matrix ww would be from the row 2, but from pp would be from row 1. Also how to tell difference between a 0 as a valid value from within the matrix elemen...