首页 文章

为什么Java不支持无符号整数?

提问于
浏览
347

为什么Java不包含对无符号整数的支持?

在我看来,这是一个奇怪的遗漏,因为它们允许人们编写不太可能在意外的大输入上产生溢出的代码 .

此外,使用无符号整数可以是一种自我文档形式,因为它们表明unsigned int意图保留的值绝不应该是负数 .

最后,在某些情况下,无符号整数对于某些操作(例如除法)可能更有效 .

包含这些内容的不利之处是什么?

15 回答

  • 50

    我知道这篇文章太旧了;但是为了您的兴趣,在Java 8及更高版本中,您可以使用 int 数据类型来表示无符号的32位整数,其最小值为0,最大值为232-1 . 使用 Integer 类将 int 数据类型用作无符号整数,并将 compareUnsigned()divideUnsigned() 等静态方法添加到 Integer 类中,以支持无符号整数的算术运算 .

  • 12

    我听说过它们将被包含在原始Java版本附近 . Oak是Java的前身,在一些规范文档中提到了使用的值 . 不幸的是,这些从未成为Java语言 . 至于任何人都能够弄清楚他们只是没有得到实施,可能是由于时间限制 .

  • -6

    这是关于简单性的interview with Gosling and others

    Gosling:对于我来说,作为一名语言设计师,我现在并不像以前那样真实地认为自己是什么“简单”真正意义上的结果是我可以期待J. Random Developer在他的脑海中保留这个规范 . 这个定义说,例如,Java不是 - 实际上很多这些语言都有很多极端情况,这些都是没人真正理解的 . 测试任何C开发人员关于unsigned的问题,很快你就会发现几乎没有C开发人员真正理解无符号算法是什么,无符号算术是什么 . 这样的事情让C变得复杂 . 我认为Java的语言部分非常简单 . 你必须查找的库 .

  • 18

    在线之间阅读,我认为逻辑是这样的:

    • 一般来说,Java设计者希望简化可用数据类型的所有组成部分

    • 对于日常用途,他们认为最常见的需求是签名数据类型

    • 用于实现某些算法,有时需要无符号算法,但是实现这种算法的程序员也会知道"work round"用签名数据类型进行无符号算术

    大多数情况下,我认为这是一个合理的决定 . 可能,我会:

    • 使字节无符号,或者至少为这一种数据类型提供了有符号/无符号的备选方案(可能有不同的名称)(使其签名有利于一致性,但何时需要有符号字节?)

    • 完成了'short'(您上次使用16位带符号算术的时间是什么时候?)

    尽管如此,通过一些克服,对高达32位的无符号值的操作也不会太糟糕,并且大多数人不需要无符号的64位除法或比较 .

  • 16

    这是一个较老的问题,拍了很简单地提到了char,我只是想我应该为其他人展望这个问题 . 让我们仔细看看Java原始类型:

    byte - 8位有符号整数

    short - 16位有符号整数

    int - 32位有符号整数

    long - 64位有符号整数

    char - 16位字符(无符号整数)

    虽然 char 不支持 unsigned 算术,但它基本上可以视为 unsigned 整数 . 您必须将算术运算显式地转换回 char ,但它确实为您提供了指定 unsigned 数字的方法 .

    char a = 0;
    char b = 6;
    a += 1;
    a = (char) (a * b);
    a = (char) (a + b);
    a = (char) (a - 16);
    b = (char) (b % 3);
    b = (char) (b / a);
    //a = -1; // Generates complier error, must be cast to char
    System.out.println(a); // Prints ? 
    System.out.println((int) a); // Prints 65532
    System.out.println((short) a); // Prints -4
    short c = -4;
    System.out.println((int) c); // Prints -4, notice the difference with char
    a *= 2;
    a -= 6;
    a /= 3;
    a %= 7;
    a++;
    a--;
    

    是的,没有对无符号整数的直接支持(显然,如果有直接支持,我不需要将大部分操作转换回char) . 但是,肯定存在无符号原始数据类型 . 我也希望看到一个无符号字节,但我想加倍内存成本,而使用char是一个可行的选择 .


    编辑

    对于JDK8,有LongInteger的新API,它们在将 longint 值视为无符号值时提供辅助方法 .

    • compareUnsigned

    • divideUnsigned

    • parseUnsignedInt

    • parseUnsignedLong

    • remainderUnsigned

    • toUnsignedLong

    • toUnsignedString

    此外,Guava提供了许多辅助方法,可以在整数类型中执行类似的操作,这有助于缩小缺少对 unsigned 整数的本机支持所留下的空白 .

  • 6

    Java确实有无符号类型,或者至少有一个:char是unsigned short . 因此无论戈斯林抛出什么借口,实际上只是他的无知为什么没有其他无符号类型 .

    短型:短裤一直用于多媒体 . 原因是您可以在一个32位无符号长度中拟合2个样本并对多个操作进行向量化 . 与8位数据和无符号字节相同 . 您可以在寄存器中放入4或8个样本进行矢量化 .

  • 2

    只要签名和未签名的整数混合在一个表达式中,事情开始变得混乱,你可能会丢失信息 . 将Java限制为签名的内容只能真正解决问题 . 我很高兴我不必担心整个签名/未签名的业务,尽管我有时会错过一个字节中的第8位 .

  • 3

    http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html

    这个人说因为C标准定义了涉及无符号和有符号整数的操作被视为无符号 . 这可能会导致负的有符号整数转换为大的unsigned int,从而可能导致错误 .

  • 181

    我认为Java很好,添加unsigned会使它复杂化而没有太大的收益 . 即使使用简化的整数模型,大多数Java程序员也不知道基本数字类型的行为 - 只需阅读书籍Java Puzzlers即可了解您可能持有的误解 .

    至于实用建议:

    • 如果您的值有些任意大小且不适合 int ,请使用 long . 如果它们不适合 long 使用 BigInteger .

    • 当需要节省空间时,仅将较小的类型用于数组 .

    • 如果您需要64/32/16/8位,请使用 long / int / short / byte 并停止担心符号位,除了除法,比较,右移和转换 .

    另见this答案关于"porting a random number generator from C to Java" .

  • 2

    有了JDK8,它确实有一些支持 .

    尽管有Gosling的担忧,我们仍然可以看到Java中对无符号类型的完全支持 .

  • 4

    我曾经和C标准委员会的某个人一起参加C课程,他暗示Java做出了正确的决定,避免使用无符号整数,因为(1)大多数使用无符号整数的程序都可以用有符号整数做得更好,这更自然人们如何思考的条件,以及(2)使用无符号整数导致很多容易创建,但很难调整诸如整数算术溢出和在有符号和无符号类型之间转换时丢失有效位的问题 . 如果你错误地使用有符号整数从0中减去1,它通常会更快地导致程序崩溃,并且比查找错误更容易找到错误,并且编译器和静态分析工具以及运行时检查必须假设你知道自从你选择使用无符号算术后你正在做什么 . 此外,像-1这样的负数通常代表一些有用的东西,比如一个字段被忽略/默认/未设置,而如果你使用无符号,你必须保留一个特殊值,如2 ^ 32 - 1或类似的东西 .

    很久以前,当内存有限且处理器不能同时自动运行64位时,每一位都计算得更多,因此签名与无符号字节或短路实际上更为重要,显然是正确的设计决策 . 今天只使用带符号的int在几乎所有常规编程案例中都是绰绰有余的,如果你的程序确实需要使用大于2 ^ 31 - 1的值,那么你通常只需要很长的时间 . 一旦你进入使用多头的领域,你就更难想出为什么你真的无法通过2 ^ 63-1正整数 . 每当我们使用128位处理器时,它都会成为一个问题 .

  • -2

    因为 unsigned 类型是纯粹的邪恶 .

    在C unsigned - int 产生 unsigned 的事实更加邪恶 .

    以下是不止一次烧毁我的问题的快照:

    // We have odd positive number of rays, 
    // consecutive ones at angle delta from each other.
    assert( rays.size() > 0 && rays.size() % 2 == 1 );
    
    // Get a set of ray at delta angle between them.
    for( size_t n = 0; n < rays.size(); ++n )
    {
        // Compute the angle between nth ray and the middle one.
        // The index of the middle one is (rays.size() - 1) / 2,
        // the rays are evenly spaced at angle delta, therefore
        // the magnitude of the angle between nth ray and the 
        // middle one is: 
        double angle = delta * fabs( n - (rays.size() - 1) / 2 ); 
    
        // Do something else ...
    }
    

    你注意到了这个bug吗?我承认我只是在踩到调试器后才看到它 .

    因为 n 是无符号类型 size_t ,所以整个表达式 n - (rays.size() - 1) / 2 的计算结果为 unsigned . 该表达式旨在成为中间一条射线的符号位置:左侧中间的第一条射线将是位置为-1,右边的第一个位置为1,等等 . 在获取abs值并乘以 delta 角度后,我将获得第1400425号光线与中间光线之间的角度 .

    不幸的是,上面的表达式包含了邪恶的无符号,而不是评估,比如-1,它被评估为2 ^ 32-1 . 随后转换为 double 密封了该错误 .

    在误用 unsigned 算术引起的一两个错误之后,必须开始想知道额外的一个比特是否值得额外的麻烦 . 我尽可能地尝试避免在算术中使用 unsigned 类型,尽管仍然将它用于非算术运算,例如二进制掩码 .

  • 14

    您的问题是“为什么Java不支持无符号整数”?

    我对你的问题的回答是Java想要所有它的原始类型: bytecharshortintlong 应该分别被视为 byteworddwordqword ,就像汇编一样,Java运算符是 signedchar 之外的所有原始类型的操作,但仅在 char 上它们仅为无符号16位 .

    所以静态方法假设是32位和64位的 unsigned 操作 also .

    您需要final类,可以为 unsigned 操作调用其静态方法 .

    您可以创建此最终类,将其命名为您想要的任何名称并实现它的静态方法 .

    如果您不知道如何实现静态方法,那么link可能会对您有所帮助 .

    在我看来,Java是 not 类似于C at all ,如果它 neither 支持无符号类型 nor 运算符重载,所以我认为Java应该被视为与C和C完全不同的语言 .

    顺便说一下,语言的名称也完全不同 .

    所以我不建议在Java中输入类似于C的代码,我不建议输入类似于C的代码,因为在Java中你将无法在C中做你想做的事情,即代码将不会继续像C一样,对我来说这样的代码是不好的,改变中间的风格 .

    我建议为签名操作编写和使用静态方法,因此除了在代码中只需要签名操作外,你不会在运算符和静态方法的代码混合中看到有符号和无符号的操作,并且可以仅使用运算符 .

    另外,我建议避免使用 shortintlong 基元类型,而是分别使用 worddwordqword ,而您将调用静态方法进行无符号运算和/或带符号运算,而不是使用运算符 .

    如果您只要进行签名操作并仅在代码中使用运算符,那么可以使用这些基本类型 shortintlong .

    实际上 worddwordqword 确实 n't 存在于该语言中,但您可以为每个创建新类,并且每个的实现应该非常简单:

    word 仅保存基本类型 short ,类 dword 仅保存基本类型 int ,类 qword 仅保存基本类型 long . 现在所有的unsigned和signed方法都是静态的或者不是你的选择,你可以在每个类中实现,即所有16位操作都是无符号的,并通过在 word 类上给出含义名称来签名,所有32位操作都是无符号的和通过在 dword 类上给出含义名称并且所有64位操作都是无符号的并通过在 qword 类上给出含义名称来签名来签名 .

    如果你不喜欢为每个方法提供太多不同的名称,你总是可以在Java中使用重载,很好地阅读Java也做了 n't 也删除了!

    如果你想要8位有符号运算的方法而不是运算符,而根本没有运算符的8位无符号运算的方法,那么你可以创建 Byte 类(注意第一个字母'B'是大写的,所以这不是基本类型 byte )并实现此类中的方法 .

    关于传递值并通过引用传递:

    如果我没有错,就像在C#中一样,原始对象自然地通过值传递,但类对象自然地通过引用传递,因此这意味着 Byteworddword 类型的对象和 qword 将通过引用传递,而不是默认值 . 我希望Java有 struct 对象,因为C#有,所以 Byteworddwordqword 可以实现为 struct 而不是 class ,所以默认情况下它们是按值传递的,而不是默认情况下通过引用传递,就像C#中的任何结构对象一样和原始类型一样,默认情况下是通过值传递而不是通过引用传递,但是因为Java比C#更糟糕,我们必须处理它,然后只有类和接口,它们通过引用传递而不是通过值传递默认情况下 . 因此,如果您想按值而不是通过引用传递 Byteworddwordqword 个对象,就像Java中的任何其他类对象以及C#中一样,您将只需使用复制构造函数就可以了 .

    这是我能想到的唯一解决方案 . 我只是希望我可以将原始类型设置为word,dword和qword,但Java既不支持typedef也不支持使用,不像C#支持 using ,这相当于C的typedef .

    关于输出:

    对于相同的 sequence of bits ,您可以通过多种方式打印它们:二进制,十进制(如C printf中%u的含义),八进制(如C printf中%o的含义),十六进制(如意义) %C in C printf)和整数(如C printf中%d的含义) .

    请注意,C printf不知道作为参数传递给函数的变量的类型,因此printf只知道传递给函数第一个参数的char *对象的每个变量的类型 .

    因此,在每个类中: Byteworddwordqword ,您可以实现print方法并获得printf的功能,即使该类的基本类型已签名,您仍然可以通过以下某些算法将其打印为unsigned逻辑和移位操作,以获取要打印到输出的数字 .

    不幸的是,我给你的链接没有显示如何实现这些打印方法,但我相信你可以谷歌搜索实现这些打印方法所需的算法 .

    这就是我能回答你的问题并建议你 .

  • 11

    我可以想到一个不幸的副作用 . 在java嵌入式数据库中,32位id字段可以拥有的id数量是2 ^ 31,而不是2 ^ 32(约20亿,不是~40亿) .

  • 6

    恕我直言的原因是因为他们懒得实施/纠正这个错误 . 建议C / C程序员不理解无符号,结构,联合,位标志...只是荒谬 .

    以太你正在和一个基本/ bash / java程序员交谈,开始编程la C,没有任何真正的知识这种语言,或者你只是出于自己的想法 . ;)

    当你开始质疑文件或硬件格式的每一天,他们在想什么 .

    这里的一个很好的例子是尝试使用无符号字节作为自旋循环 . 对于那些不理解最后一句的人,你怎么称自己为程序员 .

    DC

相关问题