R 中数值的双精度(64 位)表示(符号、指数、尾数)
Double precision (64-bit) representation of numeric value in R (sign, exponent, significand)
R FAQ 表示:
The only numbers that can be represented exactly in R’s numeric type are integers and fractions whose denominator is a power of 2. All other numbers are internally rounded to (typically) 53 binary digits accuracy.
R 使用 IEEE 754 双精度浮点数,即
- 1 位符号
- 11 位指数
- 尾数(或尾数)52 位
总和为 64 位。
对于数字0.1
,R代表
sprintf("%.60f", 0.1)
[1] "0.100000000000000005551115123125782702118158340454101562500000"
Double (IEEE754 Double precision 64-bit) 为我们提供了 0.1
的二进制表示:
00111111 10111001 10011001 10011001 10011001 10011001 10011001 10011010
我们如何在 R 中获得这种表示,它与我们示例中 sprintf
给出的输出有何关系?
@chux在评论中提出的问题的答案是"yes"; R
支持%a
格式:
sprintf("%a", 0.1)
#> [1] "0x1.999999999999ap-4"
如果要访问底层位模式,则必须将双精度值重新解释为 64 位整数。对于此任务,可以通过 Rcpp 使用 C++:
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。鉴于 exponent bias 这对应于 -4 的实际指数。
- 尾数为0x1999999999999a × 2^-52包括implicit 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"
例如考虑 0.3
。 运行 在 R 控制台中
> sprintf("%a", 0.3)
[1] "0x1.3333333333333p-2"
尾数或有效数
二进制的十六进制表示 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
从十进制到标准化双精度:
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"
R FAQ 表示:
The only numbers that can be represented exactly in R’s numeric type are integers and fractions whose denominator is a power of 2. All other numbers are internally rounded to (typically) 53 binary digits accuracy.
R 使用 IEEE 754 双精度浮点数,即
- 1 位符号
- 11 位指数
- 尾数(或尾数)52 位
总和为 64 位。
对于数字0.1
,R代表
sprintf("%.60f", 0.1)
[1] "0.100000000000000005551115123125782702118158340454101562500000"
Double (IEEE754 Double precision 64-bit) 为我们提供了 0.1
的二进制表示:
00111111 10111001 10011001 10011001 10011001 10011001 10011001 10011010
我们如何在 R 中获得这种表示,它与我们示例中 sprintf
给出的输出有何关系?
@chux在评论中提出的问题的答案是"yes"; R
支持%a
格式:
sprintf("%a", 0.1)
#> [1] "0x1.999999999999ap-4"
如果要访问底层位模式,则必须将双精度值重新解释为 64 位整数。对于此任务,可以通过 Rcpp 使用 C++:
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。鉴于 exponent bias 这对应于 -4 的实际指数。
- 尾数为0x1999999999999a × 2^-52包括implicit 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"
例如考虑 0.3
。 运行 在 R 控制台中
> sprintf("%a", 0.3)
[1] "0x1.3333333333333p-2"
尾数或有效数
二进制的十六进制表示 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
从十进制到标准化双精度:
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"