有没有一种很好的方法将int分成两个短裤(.NET)?

时间:2009-12-09 11:49:56

标签: c# .net vb.net math

我认为这是不可能的,因为Int32有1位符号并且有31位数字信息而Int16有1位符号和15位数字信息,这导致有2位符号和30位信息。

如果这是真的,那么我不能将Int32合并为两个Int16。这是真的吗?

提前致谢。

额外信息:使用Vb.Net,但我认为我可以毫无问题地翻译C#答案。

我最初想要做的是将一个UInt32转换为两个UInt16,因为这是一个与基于WORD的计算机交互的库。然后我意识到Uint不符合CLS,并尝试对Int32Int16执行相同操作。

EVEN WORSE:执行a = CType(c And &HFFFF, Int16);投掷OverflowException。我希望该语句与a = (Int16)(c & 0xffff);相同(不会抛出异常)。

13 个答案:

答案 0 :(得分:36)

这当然可以在不丢失信息的情况下完成。在这两种情况下,最终都会得到32位信息。它们是否用于符号位是无关紧要的:

int original = ...;

short firstHalf = (short) (original >> 16);
short secondHalf = (short) (original & 0xffff);

int reconstituted = (firstHalf << 16) | (secondHalf & 0xffff);

此处,reconstituted将始终等于original,因此不会丢失任何信息。

现在意味着这两条短路的标志是另一回事 - 如果firstHalf为负,original将为负,但secondHalf将是如果设置original的第15位(计数0-31),则为负数,这在原始形式中没有特别的意义。

答案 1 :(得分:12)

这应该有效:

int original = ...;
byte[] bytes = BitConverter.GetBytes(original);
short firstHalf = BitConverter.ToInt16(bytes, 0);
short secondHalf = BitConverter.ToInt16(bytes, 2);

编辑:

使用0x7FFFFFFF测试,它可以正常工作

byte[] recbytes = new byte[4];
recbytes[0] = BitConverter.GetBytes(firstHalf)[0];
recbytes[1] = BitConverter.GetBytes(firstHalf)[1];
recbytes[2] = BitConverter.GetBytes(secondHalf)[0];
recbytes[3] = BitConverter.GetBytes(secondHalf)[1];
int reconstituted = BitConverter.ToInt32(recbytes, 0);

答案 2 :(得分:7)

Jon's answer,翻译成Visual Basic,没有溢出:

Module Module1
    Function MakeSigned(ByVal x As UInt16) As Int16
        Dim juniorBits As Int16 = CType(x And &H7FFF, Int16)
        If x > Int16.MaxValue Then
            Return juniorBits + Int16.MinValue
        End If
        Return juniorBits
    End Function

    Sub Main()
        Dim original As Int32 = &H7FFFFFFF    
        Dim firstHalfUnsigned As UInt16 = CType(original >> 16, UInt16)
        Dim secondHalfUnsigned As UInt16 = CType(original And &HFFFF, UInt16)
        Dim firstHalfSigned As Int16 = MakeSigned(firstHalfUnsigned)
        Dim secondHalfSigned As Int16 = MakeSigned(secondHalfUnsigned)

        Console.WriteLine(firstHalfUnsigned)
        Console.WriteLine(secondHalfUnsigned)
        Console.WriteLine(firstHalfSigned)
        Console.WriteLine(secondHalfSigned)
    End Sub
End Module

结果:

32767
65535
32767
-1

在.NET CType(&Hffff, Int16)导致溢出,(short)0xffff给出-1(没有溢出)。这是因为默认情况下,C#编译器使用未经检查的操作并检查VB.NET。

我个人喜欢Agg's answer,因为我的代码更复杂,而Jon会在已检查环境中导致溢出异常。

我还created another answer,基于BitConverter类的代码,针对此特定任务进行了优化。但是,它使用不安全的代码。

答案 3 :(得分:4)

是的,可以使用屏蔽和位移来完成

 Int16 a,b;
 Int32 c;

 a = (Int16) (c&0xffff);
 b = (Int16) ((c>>16)&0xffff);

修改

回答评论。重建工作很好:

 Int16 a, b;
 Int32 c = -1;

 a = (Int16)(c & 0xffff);
 b = (Int16)((c >> 16) & 0xffff);

 Int32 reconst = (((Int32)a)&0xffff) | ((Int32)b << 16);

 Console.WriteLine("reconst = " + reconst);

测试它并按预期打印-1。

EDIT2:改变了重建。 Int16到Int32的升级导致所有符号位扩展。忘了,它必须是AND'ed。

答案 4 :(得分:3)

为什么不呢?为简单起见,让我们减少位数:假设我们有8位,其中左位是负位。

[1001 0110] // representing -22

您可以将其存储为2次4位

[1001] [0110] // representing   -1 and 6

我不明白为什么不可能,你两次有8位信息

编辑:为了简单起见,我不仅减少了比特,而且还不使用2补码方法。在我的例子中,左边的比特表示减号,其余部分被解释为正常的正二进制数

答案 5 :(得分:2)

您可以在VB.NET中使用StructLayout:

更正:字是16位,dword是32位

<StructLayout(LayoutKind.Explicit, Size:=4)> _
   Public Structure UDWord
      <FieldOffset(0)> Public Value As UInt32
      <FieldOffset(0)> Public High As UInt16
      <FieldOffset(2)> Public Low As UInt16

      Public Sub New(ByVal value As UInt32)
         Me.Value = value
      End Sub

      Public Sub New(ByVal high as UInt16, ByVal low as UInt16)
         Me.High = high
         Me.Low = low
      End Sub
   End Structure

签名与使用这些类型相同

<StructLayout(LayoutKind.Explicit, Size:=4)> _
   Public Structure DWord
      <FieldOffset(0)> Public Value As Int32
      <FieldOffset(0)> Public High As Int16
      <FieldOffset(2)> Public Low As Int16

      Public Sub New(ByVal value As Int32)
         Me.Value = value
      End Sub

      Public Sub New(ByVal high as Int16, ByVal low as Int16)
         Me.High = high
         Me.Low = low
      End Sub
   End Structure

修改

我有点匆匆几次发布/编辑我的anwser,然后解释这个解决方案,所以我觉得我还没有完成我的答案。所以我现在要这样做:

将StructLayout显式地用于结构需要您使用FieldOffset属性[StructLayoutAttribute]

提供每个字段(按字节偏移量)[FieldOffsetAttribute]的定位

使用这两个属性,您可以创建重叠字段,即unions

第一个字段(DWord.Value)是32位整数,偏移量为0(零)。要分割这个32位整数,你将有两个额外的字段从0(零)的偏移量再次开始,然后第二个字段2个字节关闭,因为16位(短)整数是2字节a-peice。

从我记得的情况来看,通常当你分割一个整数时,他们通常将前半部分称为“高”,然后将下半部分称为“低”;从而命名我的另外两个领域。

通过使用这样的结构,您可以为运算符创建重载并输入widing / narrowing,以便轻松地从Int32类型交换到此DWord结构,以及比较Operator Overloading in VB.NET

答案 6 :(得分:2)

C#中的不安全代码,不会发生溢出,自动检测字节顺序:

using System;
class Program
{
    static void Main(String[] args)
    {
        checked // Yes, it works without overflow!
        {
            Int32 original = Int32.MaxValue;
            Int16[] result = GetShorts(original);
            Console.WriteLine("Original int: {0:x}", original);
            Console.WriteLine("Senior Int16: {0:x}", result[1]);
            Console.WriteLine("Junior Int16: {0:x}", result[0]);
            Console.ReadKey();
        }
    }
    static unsafe Int16[] GetShorts(Int32 value)
    {
        byte[] buffer = new byte[4];
        fixed (byte* numRef = buffer)
        {
            *((Int32*)numRef) = value;
            if (BitConverter.IsLittleEndian)
                return new Int16[] { *((Int16*)numRef), *((Int16*)numRef + 1) };
            return new Int16[] { 
                (Int16)((numRef[0] << 8) | numRef[1]),  
                (Int16)((numRef[2] << 8) | numRef[3])
            };
        }
    }
}

答案 7 :(得分:2)

您可以使用StructLayout执行此操作:

[StructLayout(LayoutKind.Explicit)]
        struct Helper
        {
            [FieldOffset(0)]
            public int Value;
            [FieldOffset(0)]
            public short Low;
            [FieldOffset(2)]
            public short High;
        }

使用它,您可以获得完整的值作为int,而低部分,高部分可以作为短。

类似的东西:

var helper = new Helper {value = 12345};

答案 8 :(得分:0)

由于存储宽度(32位和16位),如果Int32大于32767,将Int32转换为Int16可能意味着丢失信息。

答案 9 :(得分:0)

如果查看位表示,那么你是对的。

你可以使用无符号整数执行此操作,因为它们没有符号位。

答案 10 :(得分:0)

如果你正在使用c ++,你可能也对StructLayout或工会感兴趣。

答案 11 :(得分:0)

Int32 num = 70000;

        string str = Convert.ToString(num, 2);
    //convert INT32 to Binary string   
        Int32 strl = str.Length;
    //detect string length


        string strhi, strlo;
    //ifvalue is greater than 16 bit 
        if (strl > 16)
        {
           int lg = strl - 16;
          //dtect bits in higher word 
           strlo = str.Substring(lg, 16);
     ///move lower word string to strlo 

            strhi = str.Substring(0, lg);
   //mov higher word string to strhi

        }
        else
//if value is less than 16 bit
        {
           strhi = "0";
//set higher word zero
           strlo = str;
///move lower word string to strlo 

        }

        Int16 lowword, hiword;
        lowword = Convert.ToInt16(strlo, 2);
        hiword = Convert.ToInt16(strhi, 2);
        ////convert binary string to int16
        }

答案 12 :(得分:0)

我没有使用按位运算符,但对于无符号值,这可能有效:

mydata = [{'stock_symbol': 'ALT', 'shares_total': 1, 'Price': 12.29}, {'stock_symbol': 'NFLX', 'shares_total': 5, 'Price': 534.5}]
stock_symbol=[]
shares_total=[]
price=[]

# start loop
for data in mydata:
   stock_symbol.append(data['stock_symbol'])
   shares_total.append(data['shares_total'])
   price.append(data['Price'])

或者它的表达式主体版本:

public (ushort, ushort) SplitToUnsignedShorts(uint value)
{
    ushort v1 = (ushort) (value / 0x10000);
    ushort v2 = (ushort) (value % 0x10000);

    return (v1, v2);
}

至于符号,您必须决定如何拆分数据。两个负输出只能有 1 个。请记住,有符号值总是牺牲一位来存储数字的负状态。这基本上将您在该变量中可以拥有的最大值“减半”。这也是为什么uint可以存储两倍于signed int的原因。

至于将其编码为您的目标格式,您可以选择将第二个数字设为无符号短整型以保留数值,或者您可以手动对其进行编码,以便现在一位代表该值的符号。这样,虽然您会丢失符号位的最初预期数值,但您不会丢失原始二进制数据,并且您始终可以将其重建为原始值。

最终归结为您希望如何存储和处理该数据。只要您知道如何从(或合并到)您的编码值中提取数据,您就不会丢失比特,进而丢失数据。