Double precision (64-bit) representation of numeric value in R (sign, exponent, significand)
The answer to the question raised by @chux in the comments is "yes"; R
supports the %a
format:
sprintf("%a", 0.1)
#> [1] "0x1.999999999999ap-4"
If you want to access the underlying bit pattern, you will have to reinterpret the double as a 64bit integer. For this task one can use C++ via 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
This hexadecimal representation is identical to your binary representation. How does one get to the decimal representation?
- The first bit is zero, hence the sign is positive
- The exponent is 0x3fb, i.e. 1019 in decimal. Given the exponent bias this corresponds to an actual exponent of -4.
- The mantissa is 0x1999999999999a × 2^-52 including the implicit 1, i.e. 2^−52 × 7,205,759,403,792,794.
In total this gives 2^−56 × 7,205,759,403,792,794:
sprintf("%.60f", 2^-56 * 7205759403792794) #> [1] "0.100000000000000005551115123125782702118158340454101562500000"
From decimal to normalized double precion:
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
}
Hence,
from10toNdp(0.1)
# "0011111110111001100110011001100110011001100110011001100110011010"