整数
通常我们不想使用双精度数,因为我们不想使用浮点运算、舍入误差等。它们只是没有必要。
为此,最好记住如何执行天花板除法:双打中的ceil(x / y) 可以写成(x + y - 1) / y(同时避免负数,但要注意溢出)。
可读
如果你追求可读性,你当然也可以这样编程(Java 中的例子,对于 C,你当然可以使用宏):
public static int ceilDiv(int x, int y) {
return (x + y - 1) / y;
}
public static int paddedBase64(int n) {
int blocks = ceilDiv(n, 3);
return blocks * 4;
}
public static int unpaddedBase64(int n) {
int bits = 8 * n;
return ceilDiv(bits, 6);
}
// test only
public static void main(String[] args) {
for (int n = 0; n < 21; n++) {
System.out.println("Base 64 padded: " + paddedBase64(n));
System.out.println("Base 64 unpadded: " + unpaddedBase64(n));
}
}
内联
填充
我们知道每次 3 个字节(或更少)需要 4 个字符块。那么公式就变成了(对于 x = n 和 y = 3):
blocks = (bytes + 3 - 1) / 3
chars = blocks * 4
或组合:
chars = ((bytes + 3 - 1) / 3) * 4
你的编译器会优化掉3 - 1,所以就这样保留它以保持可读性。
无填充
不常见的是未填充的变体,为此我们记住每个 6 位都需要一个字符,向上取整:
bits = bytes * 8
chars = (bits + 6 - 1) / 6
或组合:
chars = (bytes * 8 + 6 - 1) / 6
我们仍然可以除以二(如果我们愿意的话):
chars = (bytes * 4 + 3 - 1) / 3
不可读
如果你不相信你的编译器会为你做最后的优化(或者如果你想迷惑你的同事):
填充
((n + 2) / 3) << 2
无填充
((n << 2) | 2) / 3
所以我们有两种逻辑计算方式,我们不需要任何分支、位运算或模运算 - 除非我们真的想要。
注意事项:
- 显然,您可能需要在计算中加 1 以包含空终止字节。
- 对于 Mime,您可能需要注意可能的行终止字符等(寻找其他答案)。