Valor de Rcpp e int64 NA


8

Como posso passar um valor de NA de Rcpp para R em um vetor de 64 bits?

Minha primeira abordagem seria:

// [[Rcpp::export]]                                     
Rcpp::NumericVector foo() {
  Rcpp::NumericVector res(2);

  int64_t val = 1234567890123456789;
  std::memcpy(&(res[0]), &(val), sizeof(double));
  res[1] = NA_REAL;

  res.attr("class") = "integer64";
  return res;
}

Mas produz

#> foo()
integer64
[1] 1234567890123456789 9218868437227407266

Eu preciso pegar

#> foo()
integer64
[1] 1234567890123456789 <NA>

Você não pode usar NA_REALapós o memcpyporque o padrão de bits é nesse ponto o de a int64.
Dirk Eddelbuettel 23/04

Eu também editaria o título. O NA padrão de 64 bits é exatamente o NA_realque sua questão não é.
Dirk Eddelbuettel 23/04

Mas o memcpy copia apenas 64 bits ( sizeof(double)), certo? Então res[0]obtém 64 bits vale a configuração res[1] = ...usa os próximos 64 bits. Concordo com o resultado, mas não siga o seu primeiro comentário.
David

1
O ponto principal é que o conteúdo do vetor é então pouco a pouco um int64_tque é meramente "estacionado" dentro de um doublevetor (aka NumericVector). Não há cópia lógica mágica. Jems está fazendo todo o trabalho duro à mão. Incluindo o mapeamento de NAs.
Dirk Eddelbuettel 23/04

1
Que. É. O que. Eu tenho. Fui. Tentando. Para. Explicar. Veja, por exemplo, a fonte R para o NA existente. Veja alguns pacotes usando int64e veja o que eles fazem.
Dirk Eddelbuettel 23/04

Respostas:


6

Tudo bem, acho que encontrei uma resposta ... (não bonita, mas funcionando).

Resposta curta:

// [[Rcpp::export]]                                     
Rcpp::NumericVector foo() {
  Rcpp::NumericVector res(2);

  int64_t val = 1234567890123456789;
  std::memcpy(&(res[0]), &(val), sizeof(double));

  # This is the magic:
  int64_t v = 1ULL << 63;
  std::memcpy(&(res[1]), &(v), sizeof(double));

  res.attr("class") = "integer64";
  return res;
}

o que resulta em

#> foo()
integer64
[1] 1234567890123456789 <NA>

Resposta mais longa

Inspecionando como bit64armazena umNA

# the last value is the max value of a 64 bit number
a <- bit64::as.integer64(c(1, 2, NA, 9223372036854775807))
a
#> integer64
#> [1] 1    2    <NA> <NA>
bit64::as.bitstring(a[3])
#> [1] "1000000000000000000000000000000000000000000000000000000000000000"
bit64::as.bitstring(a[4])
#> [1] "1000000000000000000000000000000000000000000000000000000000000000"

Criado em 2020-04-23 pelo pacote reprex (v0.3.0)

nós vemos que é um 10000.... Isso pode ser recriado Rcppcom int64_t val = 1ULL << 63;. Usar em memcpy()vez de uma atribuição simples =garante que nenhum bit seja alterado!


1
Sim. Se você olhar para alguns pacotes de origem, verá a #defineinstrução correspondente para declarar um padrão de bit (geralmente um minou outro max) como o valor NA.
Dirk Eddelbuettel 23/04

6

É realmente muito, muito mais simples. Temos o comportamento de um int64em R oferecido por (vários) pacotes complementares, o melhor dos quais está bit64nos fornecendo a integer64classe S3 e o comportamento associado.

E define NA internamente da seguinte maneira:

#define NA_INTEGER64 LLONG_MIN

E isso é tudo o que existe. O R e seus pacotes são os principais códigos C, e LLONG_MINexistem lá e remontam (quase) até os pais fundadores.

Há duas lições aqui. A primeira é a extensão do IEEE que define NaN e Inf para valores de ponto flutuante . R realmente vai muito além e adiciona NApara cada um de seus tipos . Quase da maneira acima: reservando um padrão de bits específico. (Que, em um caso, é o aniversário de um dos dois criadores originais do R.)

A outra é admirar a tonelada métrica de trabalho que Jens fez com o bit64pacote e todas as funções necessárias de conversão e operador. Converter perfeitamente todos os valores possíveis, incluindo NA, NaN, Inf, ... não é uma tarefa fácil.

E é um tópico interessante que poucas pessoas sabem. Fico feliz que você fez a pergunta, porque agora temos um registro aqui.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.