2
$\begingroup$

(While there are similarities with my question here, this question deals with what Mathematica refers to as 'arbitrary precision numbers' and the former question deals with machine-precision numbers. Regardless, the behavior I'm trying to understand here is different than what I am trying to understand in the former question.)

Executing DecimalForm[N[\[Pi] - 80143857/25510582, 5]] in Mathematica 11.3 gives me the following result: 0.00000000000000057909.

Executing Precision on that result yields 5.. This makes sense because I asked for a precision of 5 as an argument to N. Executing Accuracy on the result yields 20.373. This makes sense because the number of digits to the right of the decimal is 20.

Wolfram tells us that Precision is the "total number of digits of precision" and that Accuracy is the "number of significant digits to the right of the decimal."

The understanding I've always had of significant figures/digits comports more-or-less with what is stated here: "any of the digits of a number beginning with the digit farthest to the left that is not zero and ending with the last digit farthest to the right that is either not zero or that is a zero but is considered to be exact."

If the value 0.00000000000000057909 is known to be exact (perhaps with an uncertainty around the last digit), I would simply state that it has 20 significant digits. Saying that it has a 'precision' of 5 seems strange.

If I now change the starting calculation to DecimalForm[N[\[Pi] - 80143857/25510582 + 1, 5]] we fall off of a cliff it seems, as I now get an answer of 1.0000 and Precision and Accuracy now yield, respectively, 5. and 5..

What happened to all of my accuracy digits?

If I execute FullForm on the new value, it yields 1.0000000000000005791`5. which shows that the information is the same up to the 18th digit. It makes sense to me that having added an integer component to the original would have reduced the number of known significant digits by ~1 digit or so, and it seems reasonable to look at the result as having 19 significant digits due to uncertainties around the final digit of the original 0.00000000000000057909.

Hence, can I know how many significant digits are in a result?

(FWIW, I tried to create a function that would handle the various types of results one gets from RealDigits, but was not successful.)

Thanks.

Per @MichaelE2's comment, I have added the following which is too long for a comment: The following confirms that each value has infinite precision and accuracy, not just 1:

Precision[\[Pi]] Accuracy[\[Pi]] Precision[80143857/25510582] Accuracy[80143857/25510582] Precision[1] Accuracy[1] 

When I add two of the infinite precision and accuracy values together like so:

Precision[N[\[Pi] - 80143857/25510582, 5]] Accuracy[N[\[Pi] - 80143857/25510582, 5]] 

The result shows 5 and 20.2373.

When I add three of the infinite precision and accuracy values together

Precision[N[\[Pi] - 80143857/25510582 + 1, 5]] Accuracy[N[\[Pi] - 80143857/25510582 + 1, 5]] 

the result shows 5 and 5.

If I do as @MichaelE2 suggested:

Precision[1 + N[\[Pi] - 80143857/25510582, 5]] Accuracy[1 + N[\[Pi] - 80143857/25510582, 5]] 

I get 20.2373 and 20.2373, but I don't see how that sheds light on my original question, as the original question is dealing with two and three exact values respectively that only differ by a magnitude of 1, yet have wildly different accuracy values.

$\endgroup$
6
  • $\begingroup$ Maybe this helps? $\endgroup$ Commented Jul 22, 2024 at 23:49
  • 1
    $\begingroup$ Also there is this function for SignificantDigits in the Wolfram Function Repository. $\endgroup$ Commented Jul 22, 2024 at 23:51
  • 1
    $\begingroup$ It seems to me Precision[] is correct both times, for the same reason. Twice you asked for 5 correct digits, and you got five both times. I think what you don't understand completely is how precision and accuracy work in arbitrary-precision arithmetic, how arbitrary-precision numbers are represented, and exactly how N[] works. Have you tried adding 1 to your first result, like this?: Precision[1 + N[\[Pi] - 80143857/25510582, 5]]. Note that the 1 is now outside the N[..], and that is, ahem, significant. It is also significant that 1 has infinite precision. $\endgroup$ Commented Jul 23, 2024 at 0:08
  • $\begingroup$ "wildly different accuracy values": I don't know what formula you are using to calculate the accuracies you expect, so I cannot compare with the definition (formula) used by Mathematica's Accuracy[], which is given in Numbers to which you were referred by Bob Hanlon in your related Q&A. It's probably in the function's doc page, too. $\endgroup$ Commented Jul 24, 2024 at 0:06
  • $\begingroup$ @MichaelE2, I'm just using Mathematica's Accuracy function and if you consult the definition of it, you'll see that it doesn't match what my original question shows as its outputs. $\endgroup$ Commented Jul 24, 2024 at 0:52

1 Answer 1

1
$\begingroup$

One of the questions I had in the original question was how I could obtain the number of significant digits as they are normally understood, i.e., as here.

@daniel-lichtblau pointed me in the direction of a function/package in the Wolfram repository here.

I had already embarked on trying to do my own. On 2024-07-24 I posted a solution which as it turns out was incorrect. It was @michael-e2's comment that made me re-evaluate my answer. I then spent at least seven full days trying to come up with a reliable function that would return the significant digits from any numeric quantity. I failed. Every time I thought I'd achieved success, there were corner cases that required a whole new approach.

I then looked at the Significant Digits function in the repository and found that even it has edge cases where it fails. Just two follow:

ResourceFunction["SignificantDigits"][0.00000000000000000000000010000000000000000] ResourceFunction["SignificantDigits"][0.000000000000000000000000100000000000000000] 

The former returns a value of 1 but should return 17 and the latter returns 17 but should return 18.

In the process of trying to solve this problem, I came up with some functions whose purpose is to be able to generate lots of inexact machine and arbitrary precision numbers: integer-only, fractional only, integer and fractional. I am including it here in case anyone in the future comes along to solve this problem and wants to have access to easily constructed test data.

First, an example usage. The use of Sequence @@ Values[<|...|>] is only to facilitate seeing the names of the parameters. It is not necessary.

Table[Generator[Sequence @@ Values[<| "integer" -> 0, "integerPositiveOnly" -> False, "integerCount" -> x, "integerSignificantZeroCount" -> x, "fractional" -> 1, "fractionalPositiveOnly" -> True, "fractionalCount" -> 1, "fractionalLeadingZeroCount" -> 10, "fractionalSignificantZeroCount" -> x |>]], {x, 0, 10}] 

The previous will produce the following output:

{"0.00000000001", "00.000000000010", "0000.0000000000100", "000000.00000000001000", "00000000.000000000010000", "0000000000.0000000000100000", "000000000000.00000000001000000", "00000000000000.000000000010000000", "0000000000000000.0000000000100000000", "000000000000000000.00000000001000000000", "00000000000000000000.000000000010000000000"} 

The function produces strings, but if numeric quantities are preferred, ToExpression will accomplish that.

The code for Generator and the supporting functions follows:

ClearAll[Generator, GetDigits, PortionHandler]; GetDigits[digit_Integer, digitPositiveOnly_?BooleanQ, digitCount_Integer] := Module[ {digits = "", nonNegativeIntegers = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}}, If[0 < digitCount, (* argument validation *) If[digit > 9, Throw[StringForm[ "`` is not a valid value for the digit parameter. Valid values \ are {0,1,2,3,4,5,6,7,8,9}", digit]]]; If[digit < 0, (* randomize digitCount integers *) digits = If[digitPositiveOnly, (* digits to be chosen pseudo-randomly from {"1","2","3","4", "5","6","7","8","9"} *) StringJoin @@ Table[nonNegativeIntegers[[2 ;;]][[RandomInteger[{1, 9}]]], digitCount] , (* digits to be chosen pseudo-randomly from {"0","1","2","3", "4","5","6","7","8","9"} *) StringJoin @@ Table[nonNegativeIntegers[[RandomInteger[{1, 10}]]], digitCount] ] , (* repeat digit digitCount times *) digits = StringRepeat[ToString[digit], digitCount] ] ]; digits ]; PortionHandler[digits_?StringQ, digitCount_Integer, zeros_?StringQ, zeroCount_Integer, defaultPortion_?StringQ] := Module[ {portion = defaultPortion}, If[digitCount > 0, If[zeroCount > 0, (* the portion has both digits and zeros *) portion = digits <> zeros , (* the portion has only digits *) portion = digits ] (* there is no portion *) ]; portion ]; (* Generator returns strings. To convert a result to a number use ToExpression. *) (* integer: integer digit to be generated. if <0, then the digit integer values are randomized. *) (* integerPositiveOnly: whether positive integers only are to be used for the integer part. If True, {1,2,3,4,5,6,7,8,9}. If False, {0,1,2,3,4,5,6,7,8,9}. *) (* integerCount: count of integer digits to be generated. if <=0, then no integer part. *) (* integerSignificantZeroCount: count of significant zeros for the integer part. if == 0, then no significant zeros *) (* fractional: fractional digit to be generated. if <0, then the digit integer values are randomized. *) (* fractionalPositiveOnly: whether positive integers only are to be used for the fractional part. If True, {1,2,3,4,5,6,7,8,9}. If False, {0,1,2,3,4,5,6,7,8,9}. *) (* fractionalCount: count of fractional digits to be generated. if <=0, then no fractional part. *) (* fractionalLeadingZeroCount: count of leading zeros for the fractional part. if == 0, then no leading zeros *) (* fractionalSignificantZeroCount: count of trailing zeros for the fractional part. if == 0, then no trailing zeros *) Generator[integer_Integer, integerPositiveOnly_?BooleanQ, integerCount_Integer, integerSignificantZeroCount_Integer, fractional_Integer, fractionalPositiveOnly_?BooleanQ, fractionalCount_Integer, fractionalLeadingZeroCount_Integer, fractionalSignificantZeroCount_Integer] := Module[ {integerDigits, integerPortion, integerSignificantZeros , fractionalDigits, fractionalLeadingZeros, fractionalPortion, fractionalSignificantZeros}, (* get all digit components *) integerDigits = GetDigits[integer, integerPositiveOnly, integerCount]; integerSignificantZeros = If[integerSignificantZeroCount > 0, StringRepeat["0", integerSignificantZeroCount], ""]; fractionalDigits = GetDigits[fractional, fractionalPositiveOnly, fractionalCount]; fractionalLeadingZeros = If[fractionalLeadingZeroCount > 0, StringRepeat["0", fractionalLeadingZeroCount], ""]; fractionalSignificantZeros = If[fractionalSignificantZeroCount > 0, StringRepeat["0", fractionalSignificantZeroCount], ""]; (* handle the integer portion: integer digits and significant zeros *) integerPortion = PortionHandler[integerDigits, integerCount, integerSignificantZeros, integerSignificantZeroCount, "0"]; (* handle the fractional portion: leading zeros and integer digits *) fractionalPortion = PortionHandler[fractionalDigits, fractionalCount, fractionalSignificantZeros, fractionalSignificantZeroCount, ""]; integerPortion <> "." <> fractionalLeadingZeros <> fractionalPortion ]; 
$\endgroup$
2
  • $\begingroup$ I accept this is what the OP wants, but the two standard rules, "leading zeros are not significant" and "trailing zeros in a number with the decimal point are significant", are violated. See the Britannica def cited by this answer or Wikipedia. Anyway it seemed worth noting in case someone comes searching for an implementation of the standard def. (E.g. value=0.001 gives more than one sig. dig.; value=0.100 does not give three; and value=3*0.2 gives sixteen. Machine precision seems unworkable for sig. digs.) $\endgroup$ Commented Jul 24, 2024 at 16:35
  • $\begingroup$ @MichaelE2, you are correct. My solution fails in many cases. I have edited my post. $\endgroup$ Commented Aug 5, 2024 at 22:35

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.