64-разрядное представление двойной точности числового значения в R (знак, показатель степени, значение и значение) - PullRequest
0 голосов
/ 07 мая 2018

R FAQ гласит:

Единственными числами, которые могут быть точно представлены в числовом типе R, являются целые числа и дроби, знаменатель которых является степенью 2. Все остальные числа внутренне округлены с точностью (как правило) до 53 двоичных цифр.

R использует числа с плавающей точкой двойной точности IEEE 754, равные

  • 1 бит для знака
  • 11 бит для показателя степени
  • 52 бита для мантиссы (или значения)

, который суммирует до 64 бит.

Для числового номера 0.1 R представляет

sprintf("%.60f", 0.1)
[1] "0.100000000000000005551115123125782702118158340454101562500000"

Double (IEEE754 с двойной точностью 64-бит) дает нам это двоичное представление для 0.1:

00111111 10111001 10011001 10011001 10011001 10011001 10011001 10011010

Как мы можем получить это представление в R и как оно связано с выводом, данным sprintf в нашем примере?

Ответы [ 3 ]

0 голосов
/ 08 мая 2018

Например, 0.3. Запустить в консоли R

> sprintf("%a", 0.3)
[1] "0x1.3333333333333p-2"

Mantissa или Significand

Шестнадцатеричное представление 3333333333333 в двоичном виде даст нам часть мантиссы (или значимого). Это

0011001100110011001100110011001100110011001100110011

Экспонент

Часть экспоненты (11 битов) должна быть смещением от 2^(11-1) - 1 = 1023, так как конечный 3 равен p-2 (в выводе, заданном sprintf), мы имеем

-2 + 1023 = 1021

и его двоичное представление, фиксированное в 11 битах, равно

01111111101

Вход

Что касается знакового бита, его 0 для положительного и 1 в противном случае

Представление двойной точности

Итак, полное представление:

0 | 01111111101 | 0011001100110011001100110011001100110011001100110011

Другой пример:

> sprintf("%a", -2.94)
[1] "-0x1.7851eb851eb85p+1"

# Mantissa or Significand
(7851eb851eb85) # base 16 
(0111100001010001111010111000010100011110101110000101) # base 2

# Exponent
1 + 1023 = 1024 # base 10
10000000000 # base 2

# So the complete representation is
1 | 10000000000 | 0111100001010001111010111000010100011110101110000101
0 голосов
/ 26 мая 2019

От десятичной до нормализованной двойной точности:

library(BMS)

from10toNdp <- function(my10baseNumber) {
out <- list()

# Handle special cases (0, Inf, -Inf)
if (my10baseNumber %in% c(0,Inf,-Inf)) {
if (my10baseNumber==0)    { out <- "0000000000000000000000000000000000000000000000000000000000000000" }
if (my10baseNumber==Inf)  { out <- "0111111111110000000000000000000000000000000000000000000000000000" }
if (my10baseNumber==-Inf) { out <- "1111111111110000000000000000000000000000000000000000000000000000" }
} else {

signBit <- 0 # assign initial value

from10to2 <- function(deciNumber) {
  binaryVector <- rep(0, 1 + floor(log(deciNumber, 2)))
  while (deciNumber >= 2) {
    theExpo <- floor(log(deciNumber, 2))
    binaryVector[1 + theExpo] <- 1
    deciNumber <- deciNumber - 2^theExpo  }
  binaryVector[1] <- deciNumber %% 2
  paste(rev(binaryVector), collapse = "")}

#Sign bit
if (my10baseNumber<0) { signBit <- 1 
} else { signBit <- 0 }

# Biased Exponent
BiasedExponent <- strsplit(from10to2(as.numeric(substr(sprintf("%a", my10baseNumber), which(strsplit( sprintf("%a", my10baseNumber), "")[[1]]=="p")+1, length( strsplit( sprintf("%a", my10baseNumber), "")[[1]]))) + 1023), "")[[1]] 
BiasedExponent <- paste(BiasedExponent, collapse='')
if (nchar(BiasedExponent)<11) {BiasedExponent <-  paste(c(  rep(0,11-nchar(BiasedExponent)), BiasedExponent),collapse='')    }

# Significand
significand <- BMS::hex2bin(substr( sprintf("%a", my10baseNumber) , which(strsplit( sprintf("%a", my10baseNumber), "")[[1]]=="x")+3, which(strsplit( sprintf("%a", my10baseNumber), "")[[1]]=="p")-1))

significand <- paste(significand, collapse='')
if (nchar(significand)<52) {significand <-  paste(c( significand,rep(0,52-nchar(significand))),collapse='')    }

out <- paste(c(signBit, BiasedExponent, significand), collapse='')
}

out
}

Следовательно,

from10toNdp(0.1)
# "0011111110111001100110011001100110011001100110011001100110011010"
0 голосов
/ 08 мая 2018

Ответ на вопрос, поднятый @chux в комментариях, «да»; R поддерживает формат %a:

sprintf("%a", 0.1)
#> [1] "0x1.999999999999ap-4"

Если вы хотите получить доступ к базовому битовому шаблону, вам придется интерпретировать двойное как 64-битное целое число. Для этой задачи можно использовать C ++ через Rcpp:

Rcpp::cppFunction('void print_hex(double x) {
    uint64_t y;
    static_assert(sizeof x == sizeof y, "Size does not match!");
    std::memcpy(&y, &x, sizeof y);
    Rcpp::Rcout << std::hex << y << std::endl;
}', plugins = "cpp11", includes = "#include <cstdint>")
print_hex(0.1)
#> 3fb999999999999a

Это шестнадцатеричное представление идентично вашему двоичному представлению. Как добраться до десятичного представления?

  • Первый бит равен нулю, следовательно, знак положительный
  • Показатель степени равен 0x3fb, то есть 1019 в десятичном виде. Учитывая смещение показателя , это соответствует фактическому показателю степени -4.
  • Мантисса равна 0x1999999999999a × 2 ^ -52, включая неявное 1 , то есть 2 ^ −52 × 7,205,759,403,792,794.
  • В сумме это дает 2 ^ −56 × 7,205,759,403,792,794:

    sprintf("%.60f", 2^-56 * 7205759403792794)
    #> [1] "0.100000000000000005551115123125782702118158340454101562500000"
    
...