这比你想象的更加棘手,因为它需要进行基数转换,并且基数转换是在整个数字上执行的,使用大整数算术。
当然,这并不意味着我们不能为此创建一种有效的大整数算术实现。这里提供了一个实现,它在左侧填充零(在Java Card上通常需要),并且不使用额外的内存(!)。但如果你想保存原始的大端数值,则可能需要复制它——输入值将被覆盖。将其放在RAM中是十分推荐的。
该代码只需将字节除以新的基数(10表示十进制),返回余数。余数就是下一个最低位数字。由于输入值现在已经被除以,所以下一个余数是比之前的数字位更重要的数字。它不断地除以并返回余数,直到值为零且计算完成。
算法的棘手部分是内部循环,它在原地将值除以10,同时使用尾部划分通过字节返回余数。它每次运行都提供一个余数 / 十进制数字。这也意味着该函数的顺序为O(n),其中n是结果中的数字位数(将尾部划分定义为单个操作)。请注意,n可以通过ceil(bigNumBytes * log_10(256))
计算得出:其结果也在预先计算的BCD_SIZE_PER_BYTES
表中出现。当然,log_10(256)
是一个常数十进制值,大约高达2.408
。
以下是最终的代码优化(请参见编辑以获取不同版本):
public static short toDecimalASCII(byte[] uBigBuf, short uBigOff,
short uBigLen, byte[] decBuf, short decOff) {
short dividend, division, remainder;
final short uBigEnd = (short) (uBigOff + uBigLen);
final short decDigits = BYTES_TO_DECIMAL_SIZE[uBigLen];
for (short decIndex = (short) (decOff + decDigits - 1); decIndex >= decOff; decIndex--) {
remainder = 0;
for (short uBigIndex = uBigOff; uBigIndex < uBigEnd; uBigIndex++) {
dividend = (short) ((remainder << 8) + (uBigBuf[uBigIndex] & 0xFF));
division = (short) (dividend / 10);
remainder = (short) (dividend - division * 10);
uBigBuf[uBigIndex] = (byte) division;
}
decBuf[decIndex] = (byte) (remainder + '0');
}
return decDigits;
}
private static final byte[] BYTES_TO_DECIMAL_SIZE = { 0, 3, 5, 8, 10, 13,
15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39 };
为了扩展输入尺寸,只需在表中计算并存储下一个十进制尺寸即可...
String
,那么您是否至少有char
数组和/或byte
数组? - Kevin Andersonshort[]
是String
的一个很好的替代品;即使是byte[]
也可以用于ASCII。只要我们在short
(或byte
)上也有/
(除法)和%
(余数)运算符,我们就万事大吉(;->)。 - Kevin Andersonint
和int[]
通常不支持Java Card平台。在指定数字格式时,您只是展示了一个例子:0x80FF
。这可能是一个由两个字节0x80
和0xFF
组成的无符号大端格式。您正在完全指定输出格式,请确保提供输入格式的说明。 - Maarten Bodewes