这里是@BenBolker的
base
函数的向量化版本,我将其称为
vbase
。它消除了针对提供的数字向量
x
中
n
个元素的循环,即使对于适度的
n
,在R中也可能相当昂贵。它比原始函数更通用,因为它支持正数和非正数
x
。
vbase
返回一个具有
1+digits
行(一行用于符号位)和
n
列的矩阵。该矩阵结果易于观察,并方便进行模式查找。
vbase <- function(x, base = 10, digits = 6) {
stopifnot(is.numeric(x), is.finite(x),
is.numeric(base), length(base) == 1L, base >= 2,
is.numeric(digits), length(digits) == 1L, digits >= 1)
n <- length(x)
if (n == 0L) {
return(NULL)
}
base <- as.integer(base)
digits <- as.integer(digits)
m <- 1L + digits
nz <- x != 0
res <- matrix(0L, m, n)
if (any(nz)) {
nz <- which(nz)
y <- abs(x[nz])
e <- ceiling(log(max(y), base))
pow <- base^e
res[1L, ] <- sign(x)
i <- 2L
while (i <= m) {
quo <- y %/% pow
res[i, nz] <- quo
y <- y - quo * pow
pow <- pow / base
i <- i + 1L
}
} else {
e <- 0
}
ech <- sprintf("%d", seq.int(e, by = -1, length.out = digits))
dimnames(res) <- list(EXPONENT = c("sign", ech), ELEMENT = names(x))
attr(res, "x") <- x
attr(res, "base") <- base
attr(res, "digits") <- digits
res
}
x <- 60^(-2:2)
vbase(c(-x, x), base = 60, digits = 5)
ELEMENT
EXPONENT [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
sign -1 -1 -1 -1 -1 1 1 1 1 1
2 0 0 0 0 1 0 0 0 0 1
1 0 0 0 1 0 0 0 0 1 0
0 0 0 1 0 0 0 0 1 0 0
-1 0 1 0 0 0 0 1 0 0 0
-2 1 0 0 0 0 1 0 0 0 0
attr(,"x")
[1] -2.777778e-04 -1.666667e-02 -1.000000e+00 -6.000000e+01 -3.600000e+03
[6] 2.777778e-04 1.666667e-02 1.000000e+00 6.000000e+01 3.600000e+03
attr(,"base")
[1] 60
attr(,"digits")
[1] 5
vbase(1 / 8, base = 60, digits = 10)
ELEMENT
EXPONENT [,1]
sign 1
0 0
-1 7
-2 29
-3 59
-4 59
-5 59
-6 59
-7 59
-8 59
-9 59
attr(,"x")
[1] 0.125
attr(,"base")
[1] 60
attr(,"digits")
[1] 10
矩阵结果的一个限制是,x
的最大元素(绝对值)决定了用于表示所有其他元素的指数序列。例如:
vbase(2^c(-10, 1), base = 2, digits = 2)
ELEMENT
EXPONENT [,1] [,2]
sign 1 1
1 0 1
0 0 0
attr(,"x")
[1] 0.0009765625 2.0000000000
attr(,"base")
[1] 2
attr(,"digits")
[1] 2
在这里,2^(-10)
被错误地表述了,因为指数是从1开始倒数计数的。你可以将digits
增加到12来捕获2^(-10)
和2^1
,但这样做会在结果中引入许多零,这不是内存的最佳使用方式。话虽如此,如果length(x)
不是很大,特别是当base = 60
时,你就不用太担心内存耗尽的问题。
gtools::baseOf
可能会有用(尽管这假设整数,可能需要一些技巧)。 - r2evansbc
,“echo "obase = 60; 1/8" | bc -l”会得到.07 30 00 00 00 00 00 00 00 00 00 00
的结果。我过去也用过这个方法,因为我懒得实现任意精度的进制转换,你可以参考https://rpubs.com/bbolker/4113。 - Ben Bolker