【问题标题】:Java: Unsigned numbersJava:无符号数
【发布时间】:2009-10-08 19:43:55
【问题描述】:

在 Java 中有没有办法像在 (My)SQL 中一样使用无符号数?

例如:我想使用一个 8 位变量 (byte),其范围如下:0 ... 256;而不是-128 ... 127

【问题讨论】:

    标签: java unsigned primitive-types


    【解决方案1】:

    不,除了char(有效值为 0-65535)之外,Java 没有任何无符号原始类型。这很痛苦(尤其是byte),但就是这样。

    通常,您要么坚持使用相同的大小,然后将“高”数字溢出为负数,或者使用更宽的类型(例如,byte 使用更宽的类型(例如,byte)并应对额外的内存需求。

    【讨论】:

    • 您确定它确实可以节省您的内存?我认为不会——我认为 Java 使用任何你的字长来保存一个字节(数组除外,它会打包)。在任何一种情况下,您都会受到一点速度影响而不使用整数。
    • Java 8 现在部分支持无符号算术。见stackoverflow.com/questions/25556017/…
    【解决方案2】:

    您可以使用类来模拟无符号数。例如

    public class UInt8 implements Comparable<UInt8>,Serializable
       {
       public static final short MAX_VALUE=255;
       public static final short MIN_VALUE=0;
       private short storage;//internal storage in a int 16
    
       public UInt8(short value)
          {
          if(value<MIN_VALUE || value>MAX_VALUE) throw new IllegalArgumentException();
          this.storage=value;
          }
    
       public byte toByte()
          {
          //play with the shift operator ! << 
          }
      //etc...
       }
    

    【讨论】:

    • 这是可能的。但是如果我想要一个无符号的 var,它是针对内存大小的。如果没有,我可以使用 int。
    • 对象实例的开销(可见字段以外的东西所需的内存)因 JVM 而异,但通常约为 40 字节。 4000% 的开销......好吧,可能不值得节省一些 '& 0xFF' 操作。
    • 埃里克和马丁,100% 同意。但是,如果您想确保您的代码正在处理正确类型的数据,它会很有用。例如,当您的代码正在读取/写入 C 程序使用的二进制结构时。
    【解决方案3】:

    大多数情况下,您可以像使用无符号数一样使用有符号数。大多数操作保持不变,有些需要修改。见this post

    【讨论】:

      【解决方案4】:

      在内部,您不应该使用较小的值 - 只需使用 int。据我了解,使用较小的单位只会减慢速度。它不会节省内存,因为 Java 在内部使用系统的字大小来存储所有存储(它不会打包字)。

      但是,如果您使用较小尺寸的存储单元,则必须为每个操作屏蔽它们或进行范围检查或其他操作。

      有没有注意到 char(任何操作)char 产生一个 int?他们只是真的不希望您使用这些其他类型。

      例外情况是数组(我相信它会被打包)和 I/O,您可能会发现使用较小的类型很有用...但屏蔽也可以。

      【讨论】:

      • +1 用于解释为什么使用无符号不会节省任何空间......这显然是 OP 试图做的。
      • @Stephen:对空间的担忧肯定与数组有关,除非 OP 完全无能为力。无论如何,对于大量字节大小的样本,您会使用什么类型?我想你可以使用字节并用 255 屏蔽它们,但这个问题是 Java 中的一个主要缺陷。
      【解决方案5】:

      不,你不能改变它。如果您需要大于 127 的内容,请选择大于一个字节的内容。

      【讨论】:

        【解决方案6】:

        如果您需要优化存储(例如大矩阵),您可以使用负数编码更大的正数,以节省空间。然后,您必须在需要时移动数值以获得实际值。例如,我只想处理短的正数。这在 Java 中是如何实现的:

                short n = 32767;
                n = (short) (n + 10);
                System.out.println(n);     
                int m = (int) (n>=0?n:n+65536); 
                System.out.println(m);
        

        所以当一个短整数超出范围时,它变成负数。但是,至少您可以将这个数字存储在 16 位中,并通过添加移位值(可以编码的不同值的数量)来恢复其正确值。该值应该以更大的类型(在我们的例子中为 int)恢复。这可能不是很方便,但我发现在我的情况下是如此。

        【讨论】:

          【解决方案7】:

          我对 Java 和编程很陌生。 然而,我最近遇到了同样的情况,需要无符号值。

          我花了大约两周的时间来编写我想到的所有内容,但我完全是个菜鸟,所以你可以花更少的钱。

          总体思路是创建接口,我将其命名为:UnsignedNumber&lt;Base, Shifted&gt;,并在实现抽象 AbstractUnsigned&lt;Base, Shifted, Impl extends AbstractUnsigned&lt;Base, Shifted, Impl&gt;&gt; 类的同时扩展 Number.class。

          所以,Base 参数化类型代表基本类型,Shifted 代表实际 Java 类型。 Impl 是实现这个抽象类的快捷方式。

          大部分时间都消耗 Java 8 Lambda 的样板文件以及内部私有类和安全程序。重要的是在减法或负加法等数学运算产生零极限时实现无符号行为:向后溢出有符号上限。

          最后,又花了几天时间编写工厂和实现子类。

          到目前为止,我知道: UByte 和 MUByte UShort 和 MUShort UInt 和 MUInt ...等

          它们是 AbstractUnsigned 的后代: UByte 或 MUByte 扩展 AbstractUnsigned&lt;Byte, Short, UByte&gt;AbstractUnsigned&lt;Byte, Short, MUByte&gt; UShort 或 MUShort 扩展 AbstractUnsigned&lt;Short, Integer, UShort&gt;AbstractUnsigned&lt;Short, Integer, MUShort&gt; ...等等。

          一般的想法是将无符号上限作为移位(强制转换)类型和负值的代码转置,因为它们不是来自零,而是来自无符号上限。

          更新: (感谢Ajeans 友善和礼貌的指导)

          /**
          * Adds value to the current number and returns either
          * new or this {@linkplain UnsignedNumber} instance based on
          * {@linkplain #isImmutable()}
          *
          * @param value value to add to the current value
          * @return new or same instance
          * @see #isImmutable()
          */
          public Impl plus(N value) {
              return updater(number.plus(convert(value)));
          }
          

          这是AbstractUnsigned&lt;N, Shifted, Impl&gt;的一个外部可访问的方法(或者正如之前所说的AbstractUnsigned&lt;Base, Shifted, Impl&gt;); 现在,到引擎盖下的工作:

          private Impl updater(Shifted invalidated){
              if(mutable){
                  number.setShifted(invalidated);
                  return caster.apply(this);
              } else {
                  return shiftedConstructor.apply(invalidated);
              }
          }
          

          在上述私有方法中mutableAbstractUnsignedprivate final booleannumber 是负责将Base 转换为Shifted 的内部私有类之一,反之亦然。 与之前的“我去年夏天所做的部分”相对应的重要事项 是两个内部对象:castershiftedConstructor

          final private Function<UnsignedNumber<N, Shifted>, Impl> caster;
          final private Function<Shifted, Impl> shiftedConstructor;
          

          如果AbstractUnsigned&lt;&gt; 的当前实现实例不可变,这些是将N(或Base)转换为Shifted 或创建新的Impl 实例的参数化函数。

          Shifted plus(Shifted value){
              return spawnBelowZero.apply(summing.apply(shifted, value));
          }
          

          在这个片段中显示了number 对象的添加方法。这个想法是始终在内部使用Shifted,因为不确定何时会产生“原始”类型的正限制。 shifted 是一个内部参数化字段,它承载整个AbstractUnsigned&lt;&gt; 的值。另外两个Function&lt;&gt;派生对象如下:

          final private BinaryOperator<Shifted> summing;
          final private UnaryOperator<Shifted> spawnBelowZero;
          

          前者执行两个Shifted 值的相加。后者在零转置以下执行产卵。

          现在是来自AbstractUnsigned&lt;Byte, Short&gt; 的工厂样板“地狱”之一的示例,专门针对前面提到的spawnBelowZero UnaryOperator&lt;Shifted&gt;

          ...,
                   v-> v >= 0
                   ? v
                   : (short) (Math.abs(Byte.MIN_VALUE) + Byte.MAX_VALUE + 2 + v),
          ...
          

          如果Shifted v 是肯定的,则不会真正发生任何事情,并且会返回原始值。否则:需要计算Base 类型的上限Byte,并将该值加起来为负v。如果,比方说,v == -8,那么Math.abs(Byte.MIN_VALUE) 将产生128Byte.MAX_VALUE 将产生127,它给出255 + 1 来获得被符号位削减的原始上限,因为我明白了,那么令人向往的256 就在那儿。但是第一个负值实际上是256,这就是为什么再次+1 或总共+2。最后,255 + 2 + v-8 给出255 + 2 + (-8)249

          或者以更直观的方式:

          0 1 2 3 ... 245 246 247 248 249 250 251 252 253 254 255 256
                                       -8  -7  -6  -5  -4  -3  -2  -1
          

          最后确定一下:这绝对不会减轻您的工作或节省内存字节,但在需要时您有一个非常理想的行为。您几乎可以将此行为与任何其他 Number.class 子类一起使用。 AbstractUnsignedNumber.class 的子类,它本身提供了所有方便的方法和常量 类似于其他“本机”Number.class 子类,包括MIN_VALUEMAX_VALUE 等等,例如,我为可变子类编写了便捷方法makeDivisibileBy(Number n),它执行value - (value % n) 的最简单操作。

          我最初的努力是为了证明即使是像我这样的菜鸟也可以编写代码。在编写该类代码时,我最初的努力是获得方便的多功能工具以供持续使用。

          【讨论】:

          • 这读起来像“我去年夏天做了什么”,因此其中包含的任何答案都很难找到。我建议删除所有无关的内容,例如“我花了两周时间”等。此外,不仅要解释 what 你做了什么,而且如何,以及这如何为这个古老的问题添加新信息
          猜你喜欢
          • 2010-10-13
          • 1970-01-01
          • 2021-12-06
          • 2011-05-25
          • 1970-01-01
          • 1970-01-01
          • 2014-02-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多