将整数转换为BCD字节数组

5

我想使用BCD将一个int转换为byte[2]数组。

这个int将来自代表年份的DateTime,必须转换为两个字节。

有没有现成的函数可以做到这一点,或者你能否给我一个简单的方法来完成这个任务?

示例:

int year = 2010

将输出:

byte[2]{0x20, 0x10};
9个回答

11
    static byte[] Year2Bcd(int year) {
        if (year < 0 || year > 9999) throw new ArgumentException();
        int bcd = 0;
        for (int digit = 0; digit < 4; ++digit) {
            int nibble = year % 10;
            bcd |= nibble << (digit * 4);
            year /= 10;
        }
        return new byte[] { (byte)((bcd >> 8) & 0xff), (byte)(bcd & 0xff) };
    }

请注意,你请求的是大端字节序结果,这有点不寻常。


3
如果一个人在处理嵌入式硬件时使用大端字节序可能并不那么不寻常。 - Matthew Whited
非常好,谢谢!在这种情况下,我认为字节序并不重要,虽然我会小心处理它,但在这种情况下没问题。 - Roast

8
请使用这个方法。
    public static byte[] ToBcd(int value){
        if(value<0 || value>99999999)
            throw new ArgumentOutOfRangeException("value");
        byte[] ret=new byte[4];
        for(int i=0;i<4;i++){
            ret[i]=(byte)(value%10);
            value/=10;
            ret[i]|=(byte)((value%10)<<4);
            value/=10;
        }
        return ret;
    }

这就是它的基本工作原理。
- 如果值小于0或大于99999999,则该值不适合四个字节。更正式地说,如果值小于0或者是10 ^(n * 2)或更大,其中n是字节数,则该值不适合n个字节。 - 对于每个字节: - 将该字节设置为value除以10的余数。(这将把最后一位数字放在当前字节的低半字节中。) - 将value除以10。 - 将16乘以value除以10的余数加到该字节中。(这将把现在的最后一位数字放在当前字节的高半字节中。) - 将value除以10。
(一种优化方法是事先将每个字节设置为0——当.NET分配新数组时隐含地完成——并在值达到0时停止迭代。出于简单起见,上面的代码未执行后一种优化。此外,如果可用,某些编译器或汇编程序提供了一个除法/余数例程,允许在一次除法步骤中检索商和余数,这是一种通常不必要的优化。)

你能逐步告诉我这里发生了什么吗?谢谢。 - Anonymous
@匿名用户:我已经添加了一个解释。 - Peter O.
厉害的函数 - 我用了很久 - 我将自己的更改为通过增加数组到5个字节来覆盖所有int值。我还创建了其他数据类型的类似函数,并将它们制作成扩展方法。感谢让我开始。 - Mladen Mihajlovic

3

这是一个可怕的暴力版本。我相信肯定有比这更好的方法,但它应该仍然有效。

int digitOne = year / 1000;
int digitTwo = (year - digitOne * 1000) / 100;
int digitThree = (year - digitOne * 1000 - digitTwo * 100) / 10;
int digitFour = year - digitOne * 1000 - digitTwo * 100 - digitThree * 10;

byte[] bcdYear = new byte[] { digitOne << 4 | digitTwo, digitThree << 4 | digitFour };

很遗憾,快速的二进制到BCD转换已经内置在x86微处理器架构中,只要你能够使用!

3
这是一个稍微更清晰的版本,然后Jeffrey's
static byte[] IntToBCD(int input)
{
    if (input > 9999 || input < 0)
        throw new ArgumentOutOfRangeException("input");

    int thousands = input / 1000;
    int hundreds = (input -= thousands * 1000) / 100;
    int tens = (input -= hundreds * 100) / 10;
    int ones = (input -= tens * 10);

    byte[] bcd = new byte[] {
        (byte)(thousands << 4 | hundreds),
        (byte)(tens << 4 | ones)
    };

    return bcd;
}

1
你可能可以想出一些使用布尔逻辑和移位的疯狂方法...但这种方式更易于维护。 - Matthew Whited

2
也许可以编写一个简单的解析函数,其中包含此循环。
i=0;
while (id>0)
{
    twodigits=id%100; //need 2 digits per byte
    arr[i]=twodigits%10 + twodigits/10*16;  //first digit on first 4 bits second digit shifted with 4 bits
    id/=100;

    i++;
}

1

更常见的解决方案

    private IEnumerable<Byte> GetBytes(Decimal value)
    {
        Byte currentByte = 0;
        Boolean odd = true;
        while (value > 0)
        {
            if (odd)
                currentByte = 0;

            Decimal rest = value % 10;
            value = (value-rest)/10;

            currentByte |= (Byte)(odd ? (Byte)rest : (Byte)((Byte)rest << 4));

            if(!odd)
                yield return currentByte;

            odd = !odd;
        }
        if(!odd)
            yield return currentByte;
    }

如果你只能计算整数,那么就不应该使用小数作为输入参数。如果你需要在内部计算中使用小数,它应该保持在内部,或者你如何计算 GetBytes(3.55549322); - Oliver

1

和Peter O.相同版本,但使用VB.NET

Public Shared Function ToBcd(ByVal pValue As Integer) As Byte()
    If pValue < 0 OrElse pValue > 99999999 Then Throw New ArgumentOutOfRangeException("value")

    Dim ret As Byte() = New Byte(3) {} 'All bytes are init with 0's

    For i As Integer = 0 To 3
      ret(i) = CByte(pValue Mod 10)
      pValue = Math.Floor(pValue / 10.0)
      ret(i) = ret(i) Or CByte((pValue Mod 10) << 4)
      pValue = Math.Floor(pValue / 10.0)
      If pValue = 0 Then Exit For
    Next

    Return ret
End Function

这里的诀窍是要注意,仅使用pValue /= 10将会四舍五入该值,因此如果参数为“16”,字节的第一部分将是正确的,但除法的结果将是2(1.6将被向上舍入)。因此我使用Math.Floor方法。

0

我写了一个通用程序,发布在IntToByteArray,你可以像这样使用:

var yearInBytes = ConvertBigIntToBcd(2010, 2);


-3
static byte[] IntToBCD(int input) { 
    byte[] bcd = new byte[] { 
        (byte)(input>> 8), 
        (byte)(input& 0x00FF) 
    };
    return bcd;
}

2
这不是二进制编码十进制。你只是将整数转换为两个字节的数组。 - Scott Chamberlain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接