Exceed the 7 digit decimal limit for numeric values in R? - r

Just noticed that R limits numeric values to 7 digits below the decimal. I'm needing to calculate and output numeric values of down to 16 digits. Is it possible to exceed the supposed 7 digit decimal limit in R?
As you can see in the example below, it won't output
any digits below 7.
> 0.6431159420289856
[1] 0.6431159
Desired output of course is
> 0.6431159420289856
[1] 0.6431159420289856
My particular use case requires those values to be outputted.

You can change the decimal places displayed with options(digits = 16) to get your requested output. That said, R will do math on all the digits available, regardless of the options setting for decimal places.
options(digits = 16)
0.6431159420289856
[1] 0.6431159420289856

Related

How to make R display 19 digit number as it is stored?

I have a dataset with a key column which is basically a 19 digit integer.
I'm using tibbles so I use options(pillar.sigfig = 22) to display larger numbers and not scientific notation.
Problem is, I notice that the number stored in the column and the one that is displayed are slightly different, to be specific last 3 digits are different.
E.g
options(pillar.sigfig = 22)
x <- 1099324498500011011
But when I try to return the number I get 1099324498500011008.
I'm not sure why R would change the last 3 digits and since it is a key, it makes my data unusable for analysis.
I have tried the usual options(scipen = 999) for suppressing scientific notation but it does not seem to work on tibbles.
How do I get the same 19 digit number as I intend to store it?
Sorry to be bearer of bad news but R only has
a numeric type (double) using 64 bits and approximately sixteen decimals precision
an integer type (int) using 32 bits
There is nothing else. You may force the print function to show you nineteen digits but that just means ... you are looking at three digits of randomness.
19 digits for (countable) items are common, and often provided by (signed or unsigned) int64_t types. Which R does not have natively but approximates via the integer64 call in the bit64 package.
So the following may be your only workaround:
> suppressMessages(library(bit64))
> x <- as.integer64("123456790123456789")
> x
integer64
[1] 123456790123456789
> x - 1
integer64
[1] 123456790123456788
>
The good news is that integer64 is reasonably well supported by data.table and a number of other packages.
PS It really is 19 digits where it bites:
> as.integer64(1.2e18) + 1
integer64
[1] 1200000000000000001
> as.integer64(1.2e19) + 1
integer64
[1] <NA>
Warning message:
In as.integer64.double(1.2e+19) : NAs produced by integer64 overflow
>

Can as.numeric(as.character(x)), where x is originally a numeric, ever change x?

I am wondering if converting numerics to characters and then back again in R can ever change the number? For example, does as.character round off numerics after a certain amount of decimal places (if so, how many)?
#jogo thanks for the suggestion :)
Here is the comment as an answer:
From ?as.character():
as.character represents real and complex numbers to 15 significant
digits (technically the compiler's setting of the ISO C constant
DBL_DIG, which will be 15 on machines supporting IEC60559 arithmetic
according to the C99 standard). This ensures that all the digits in
the result will be reliable (and not the result of representation
error), but does mean that conversion to character and back to numeric
may change the number. If you want to convert numbers to character
with the maximum possible precision, use format.
So yes it does change the number if you have more than 15 significant digits. See:
> as.character(1.000000000000001) # more than 15 significant digits
[1] "1"
> as.character(1.00000000000001) # less than 15 significant digits
[1] "1.00000000000001"
Here are some other examples:
y <- as.numeric(as.character(pi))
identical(y, pi) ### gives FALSE
or
x <- 1/7
y <- as.numeric(as.character(x))
x-y
or
as.numeric(as.character(.Machine$double.xmax)) ## result: Inf

Wrong number of digits in format.pval()?

When specifying 3 digits in format.pval(), why does, say, 0.019950 outputs 4 digits:
format.pval(0.019950, eps=.001, digits=3, nsmall=3)
"0.0199"
But, say, 0.019951 outputs 3 digits:
format.pval(0.019951, eps=.001, digits=3, nsmall=3)
"0.020"
Edit (Solution):
The solution to having 3 digits while preserving the p-value formatting, based on dcarlson's answer, was simply to round the value to 3 digits before passing it to format.pval():
format.pval(round(0.019950, digits=3), eps=.001, digits=3, nsmall=3)
"0.020"
To address your first question "why does, say, 0.019950 outputs 4 digits": With the argument digits = 3 you specify that you'd like to show the first 3 significant digits after the decimal point (which are 199). From ?format.pval
digits: how many significant digits are to be used.
In response to your second question: 0.019951 with digits = 3 first gets rounded to "0.02" (you can confirm that by looking at the output of format.pval(0.019951, eps=.001, digits=3)). Then by setting nsmall = 3 you ask for 3 digits after the decimal point, which turns "0.02" into "0.020".
The digits= argument here and elsewhere in R is SIGNIFICANT digits, not decimal digits. Significant digits ignore leading 0's in a decimal so the first response is 3 significant digits since you do not count the 0 following the decimal point. In the second example the answer is rounded up to .02, but nsmall=3 forces the additional trailing 0.
In addition, specifying a digits= argument in R is usually treated as advisory so it may not be followed. You can always force R to print the number of decimals using round or the formatting function sprintf.
format.pval(round(0.019950, 3), digits=3, nsmall=3)
# [1] "0.020"
sprintf("%.3f", .019950)
# [1] "0.020"

r keeping only specific digits after the decimal point and printing the number

In R, How can I ensure that i only print 3 characters/numbers after the decimal point?
I thought that format command with nsmall parameter is sufficient but i am not getting required answer
> format(0.6791787, nsmall=3)
[1] "0.6791787"
I want 0.679
Using sprintf it would be
sprintf("%.3f", 0.6791787)
# [1] "0.679"
The number after the . and before the f is the number of digits to print after the decimal.

rounding of digits

I'm having troubles with
set.seed(1)
sum(abs(rnorm(100)))
set.seed(1)
cumsum(abs(rnorm(100)))
Why does the value of the sum differ from the last value of the cumulative sum with the cumulative sum preserving the all decimal digits and sum rounding 1 digit off.
Also note that this really really is about how values are printed i.e. presented. This does not change the values themselves, e.g. ...
set.seed(1)
d1 <- sum(abs(rnorm(100)))
set.seed(1)
d2 <- cumsum(abs(rnorm(100)))
(d1 == d2)[100]
## [1] TRUE
This is a consequence of the way R prints atomic vectors.
With the default digits option set to 7 as you likely have, any value between -1 and 1 will print with seven decimal places. Because of the way R prints atomic vectors, all other values in the vector will also have seven decimal places. Furthermore, a value of .6264538 with digits option set to 7 must print with eight digits (0.6264538) because it must have a leading zero. There are two of these values in your rnorm() vector.
If you look at cumsum(abs(rnorm(100)))[100] alone and you can see the difference (actually it becomes the same as printed value as sum(abs(rnorm(100))), although not exactly the same value).
sum(abs(rnorm(100)))
# [1] 71.67207
cumsum(abs(rnorm(100)))[100]
# [1] 71.67207
Notice that both of these values have seven digits. Probably the most basic example of this I can think of is as follows
0.123456789
#[1] 0.1234568
1.123456789
#[1] 1.123457
11.123456789
# [1] 11.12346
## and so on ...

Resources