3

For example: I have the number 123.456 and want to return 456.

The function trunc() basically isolates (truncates) the numbers before the decimal.

Is there a function to isolate just the digits after the decimal?

Two follow-up questions:

  1. Is there a way to do this without writing out the regex?

  2. What if I want to maintain the sign? For example, if I wanted to (reverse) truncate -123.456 to -456.

theforestecologist
  • 4,667
  • 5
  • 54
  • 91
  • `sign(x) * (abs(x) %% 1)` – Gregor Thomas Sep 30 '16 at 20:07
  • @Gregor wouldn't that be `x-trunc(x)`? – lukeA Sep 30 '16 at 20:09
  • Ooops, missed that an integer is wanted. Seems like floating point arithmetic issues waiting to mess you up. Nice that many answers are taking care of that, but leaving it as a decimal seems safer. – Gregor Thomas Sep 30 '16 at 20:12
  • 1
    Out of curiosity: What would be a use case for this? – Roland Sep 30 '16 at 20:23
  • @Roland, well really I only need it to test the presence of non whole-number integers. I was interested in how to extend that concept a bit further mostly out of curiosity. But, then I figured I couldn't come up with sleek ways to do as the question asks, so I asked. – theforestecologist Sep 30 '16 at 23:20

3 Answers3

7

Using the modulo operator (%%) with 1 as the devisor works I guess. It does successfully isolate the numbers following the decimal, but leaves the decimal:

123.456 %% 1
[1] 0.456

However it only works for positive numbers in that the sign is not preserved nor are the proper numbers reported (b/c of modulo operator's functionality):

-123.456 %% 1
[1] 0.544  #not what is wanted

Including abs() fixes that issue, but doesn't help report sign. Sign could be included by adding:

sign(x) * (abs(x) %% 1)

If we really wanted to report just the digits after the decimal (i.e., excluding the 0 and the decimal), we could do the following (presented in 2 steps for clarity):

x <- -123.456
y <- sign(x) * (abs(x) %% 1)
as.numeric(gsub("0.","",y))
[1] -456  #as desired from part 2 in the OP
theforestecologist
  • 4,667
  • 5
  • 54
  • 91
  • We could easily turn this into a FUNCTION that allows you to choose whether to include the sign in the output or not. I'll call the function "decIso" for decimal isolator: `decIso <- function(x, sign = TRUE) { y <- (abs(x) %% 1) ; z <- ifelse(sign == T, sign(x) * y, y) ; as.numeric(gsub("0.","",z)) }` – theforestecologist Feb 01 '22 at 18:27
4

I don't like text processing for these kinds of operations.

The tricky part is converting the fractional number into the integer. Here I use a loop. It's unlikely that many iterations are needed. So, performance is probably not an issue.

fun <- function(x) {
  y <- abs(x) - floor(abs(x))

  n <- 0
  while (abs(y - round(y, n)) > .Machine$double.eps^0.5) {
    n <- n + 1
  }

  sign(x) * y * 10^n
}

fun(123.456)
#[1] 456
fun(-123.456)
#[1] -456
Roland
  • 127,288
  • 10
  • 191
  • 288
1

This is another way to go

> x <- c(123.456, -123.456)
> sign(x) * as.integer(sapply(strsplit(as.character(x), "\\."), "[", 2))
[1]  456 -456
Jilber Urbina
  • 58,147
  • 10
  • 114
  • 138