为什么Java不支持unsigned int?

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

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

此外,使用无符号整数可以是一种自我logging的forms,因为它们表明无符号整数打算保存的值永远不应该是负值。

最后,在某些情况下,无符号整数可以更有效地执行某些操作,例如除法。

包括这些的缺点是什么?

这来自对高斯林和其他人的采访 ,关于简单:

Gosling:对于我这样一个语言devise师来说,我真的不把自己算在这些日子里,真正意义上的“简单”意味着什么,我可以指望J.Random Developer在他的脑海中保持这个规范。 这个定义说,例如,Java并不是 – 实际上很多这些语言最终都会遇到大量的angular落案例,这些都是没有人真正理解的。 测验任何C开发者关于未签名的,很快你会发现几乎没有C开发者真正理解什么是无符号的,什么是无符号的算术。 像这样的事情使C复杂。 Java的语言部分我觉得很简单。 你必须查找库。

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

  • 一般来说,Javadevise者希望简化可用数据types的库
  • 对于日常用途,他们觉得最常见的需求是签名数据types
  • 为了实现某些algorithm,有时需要无符号算术,但是实现这种algorithm的程序员也可以知道如何“循环”地执行带符号数据types的无符号算术

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

  • 使字节无符号,或至less提供了一个签名/未签名的替代品,可能具有不同的名称,这一个数据types(使它的签名有利于一致性,但什么时候你需要一个有符号的字节?)
  • “短”(你最后一次使用16位有符号算术?)

尽pipe如此,对于高达32位的无符号值的操作并不是太差,大多数人不需要无符号的64位除法或比较。

这是一个较老的问题,拍拍也简单地提到了字符,我只是认为我应该扩大这个对于其他人来看这个道路上。 让我们仔细看看Java基本types:

byte – 8位有符号整数

short 16位有符号整数

int – 32位有符号整数

long 64位有符号整数

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

尽pipechar不支持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)。 但是,当然存在一个无符号的原始数据types。 我也喜欢看到一个无符号的字节,但我想加倍的内存成本,而不是使用字符是一个可行的select。


编辑

在JDK8中, LongInteger有一些新的API,它们将longint值作为无符号值提供帮助。

  • compareUnsigned
  • divideUnsigned
  • parseUnsignedInt
  • parseUnsignedLong
  • remainderUnsigned
  • toUnsignedLong
  • toUnsignedString

此外, Guava提供了许多帮助器方法来为整数types做类似的事情,这有助于缩小缺less对unsigned整数本机支持所留下的空白。

只要签名和未签名的整数在expression式中混杂在一起,就会变得杂乱无章,您可能丢失信息。 将Java限制为签名的整数只能真正解决问题。 我很高兴我不必担心整个签名/未签名的业务,虽然我有时会错过一个字节的第8位。

Java确实有unsignedtypes,或者至less有一个:char是一个unsigned short。 所以无论Gosling抛出什么借口,这真的只是他的无知,为什么没有其他无符号types。

另外短型:短裤一直用于多媒体。 原因是你可以在一个32位无符号长整型中插入2个样本,并且可以对许多操作进行向量化。 与8位数据和无符号字节一样的东西。 您可以将4个或8个样本放入寄存器中进行vector化。

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

这家伙说,因为C标准定义涉及无符号和有符号整数的操作被视为无符号。 这可能会导致负符号整数滚到一个大的unsigned int,可能会导致错误。

我认为Java是好的,添加unsigned会使它复杂化,没有太大的收获。 即使使用简化的整数模型,大多数Java程序员也不知道基本的数字types是如何工作的 – 只要阅读Java Puzzlers一书,看看您可能持有哪些误解。

至于实际的build议:

  • 如果你的值有些随意的大小,并不适合int ,使用long 。 如果他们不适合long使用BigInteger

  • 当您需要节省空间时,只能将较小的types用于数组。

  • 如果你正好需要64/32/16/8位,使用long / int / short / byte ,除了除法,比较,右移和铸造外,不要再担心符号位。

另请参阅“将随机数生成器从C移植到Java”的答案。

有了JDK8,它对它们有一些支持。

尽pipe有Gosling的担忧,但我们仍然可以看到对Java中无符号types的全面支持。

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

我听说过他们将被包括在接近Java版本的故事中。 Oak是Java的先驱,在一些规范文档中提到了被赋予的值。 不幸的是,这些从来没有成为Java语言。 就任何人都能弄清楚,他们只是没有得到落实,可能是由于时间的限制。

我曾经和C ++标准委员会的某个人一起参加了一个C ++课程,这意味着Java做出了避免使用无符号整数的正确决定,因为(1)大多数使用无符号整数的程序可以和有符号整数一样好,这更自然(2)使用无符号整数会导致很多很容易创build,但很难debugging整数算术溢出等问题,并且在有符号和无符号types之间转换时会丢失重要的位。 如果错误地使用带符号整数从0减1,那么通常会更快地导致程序崩溃,并且比find2 ^ 32 – 1更容易findbug,编译器和静态分析工具以及运行时检查必须假设你知道你在做什么,因为你select使用无符号算术。 此外,像-1这样的负数通常可以表示一些有用的东西,比如忽略/默认/未设置的字段,而如果使用的是未签名的,则必须保留一个特殊的值,如2 ^ 32 – 1或类似的东西。

很久以前,当内存有限,处理器一次不能自动运行在64位时,每一位都计算得更多,所以有符号和无符号字节或者短路实际上经常变得非常重要,显然是正确的devise决策。 今天,在几乎所有常规编程的情况下,使用带符号的int都是绰绰有余的,如果你的程序确实需要使用大于2 ^ 31 – 1的值,那么你经常只需要很长的一段时间。 一旦你进入了使用多头的领域,更难以想出一个你为什么不能以2 ^ 63 – 1的正整数通过的理由。 每当我们去128位处理器,这将是更less的问题。

由于unsignedtypes是纯粹的邪恶。

在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 ... } 

你有没有注意到这个错误? 我承认我只是在debugging器之后才看到它。

由于n是无符号typessize_t因此整个expression式n - (rays.size() - 1) / 2评估为unsigned 。 这个expression的目的是作为第n射线与中间射线的有符号位置:左边中间的第一条射线的位置为-1,右边的第一条射线的位置为+1,等等。在获取绝对值并乘以deltaangular后,我会得到第n射线和中间的angular度。

对我来说不幸的是,上面的expression式包含了无符号的邪恶值,而不是对-1进行求值,而是将其计算为2 ^ 32-1。 随后转换为double密封错误。

经过一个错误使用unsigned算术引起的一两个错误之后,就不得不开始怀疑这个额外的位是否值得额外的麻烦。 我尽可能地尽量避免在算术中使用unsignedtypes,尽pipe仍然将它用于非算术运算,如二进制掩码。

你的问题是“为什么Java不支持unsigned int”?

我的回答是,Java希望它的所有原始types: bytecharshortintlong应该分别对待为byteworddwordqword ,就像在assembly中一样,并且Java运算符是signedchar之外的所有基本types的操作,但只有在字符上,它们只有16位无符号。

所以静态方法假设也是 32位和64位的无符号操作。

你需要最终的类,其静态方法可以被称为未签名的操作。

你可以创build这个最终的类,把它叫做任何你想要的名字,并实现它的静态方法。

如果你不知道如何实现静态方法,那么这个链接可能会帮助你。

在我看来,Java根本不像 C ++ 既不支持无符号types也不支持运算符重载,所以我认为Java应该被视为与C ++和C完全不同的语言。

顺便说一下,语言的名字也完全不同。

所以我不推荐用Java来input类似于C的代码,我不build议键入与C ++类似的代码,因为在Java中你将无法在C ++中做下一步的工作,即代码将不会继续像C ++一样,对我来说这是不好的代码,改变中间的风格。

我build议编写和使用静态方法也用于已签名的操作,所以在代码中看不到运算符和静态方法的混合体,除非在代码中只需要签名操作,并且没关系只使用操作员。

另外,我build议避免使用shortintlong原始types,而是分别使用worddwordqword ,而不是使用运算符,而是针对未签名操作和/或有符号操作调用静态方法。

如果你只是做了签名操作,只在代码中使用操作符,那么可以使用shortintlong这些基本types。

实际上, worddwordqword在语言中并不存在,但是您可以为每个类创build新的类,并且每个类的实现都应该很容易:

字只包含原始types ,类dword只包含原始typesint而类qword只包含原始types。 现在所有的无符号和有符号的方法都是静态或非静态的,你可以在每个类中实现,即所有的16位操作都是无符号和有符号的,通过在dword类上给出含义名称来进行签名,以及通过在qword类上给出含义名称来对所有64位操作进行无符号签名和签名。

如果你不喜欢为每种方法提供太多不同的名称,你总是可以在Java中使用重载,很好的阅读Java也不会删除它!

如果你想要的方法,而不是运营商的8位有符号操作和8位无符号操作的方法根本没有运营商,那么你可以创build字节类(注意,第一个字母'B'是大写,所以这不是原始types字节 )并实现该类中的方法。

关于传递价值和传递参考:

如果我没有错,就像在C#中一样,原生对象自然是通过值传递的,但是类对象是通过引用自然地传递的,所以这意味着Byteworddwordqwordtypes的对象将被引用而不是按值传递默认。 我希望Java有结构对象作为C#有,所以所有字节单词双字qword可以被实现为结构,而不是 ,所以默认情况下,他们是通过值传递,而不是默认的引用,就像在C# ,像原始types一样,是通过值传递的,而不是默认的引用,但是因为Java比C#更糟糕,我们必须处理这个问题,那么只有类和接口,通过引用而不是通过值传递默认。 所以如果你想通过值传递Byteworddwordqword对象而不是通过引用,就像在Java和C#中的任何其他类对象,你将不得不简单地使用复制构造函数,就是这样。

这是我能想到的唯一解决scheme。 我只是希望能够将基本types的typedeftypes转换为word,dword和qword,但是Java不支持typedef也不支持使用 ,与支持使用的 C#不同,它与C的typedef相同。

关于输出:

对于相同的位序列 ,可以用多种方式打印它们:如二进制,作为十进制(如C printf中%u的含义),作为八进制(如C printf中%o的含义),如hex(如C printf中%x的含义)和整数(如C printf中%d的含义)。

请注意,C printf不知道作为parameter passing给函数的variables的types,所以printf只知道传递给函数第一个参数的char *对象的每个variables的types。

因此,在每个类: Byteworddwordqword中 ,可以实现print方法并获得printf的function,即使该类的原始types已签名,仍然可以通过以下涉及的algorithm将其打印为无符号逻辑和移位操作来获取要输出的数字。

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

这就是我可以回答你的问题,并build议你。

我可以想到一个不幸的副作用。 在javaembedded式数据库中,你可以用一个32位的id字段的数量是2 ^ 31,而不是2 ^ 32(〜20亿,而不是〜40亿)。

恕我直言,原因是因为他们太懒了,不能执行/纠正这个错误。 build议C / C ++程序员不理解无符号,结构,联合,位标志…只是荒谬的。

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

当你每天从文件或硬件上处理格式时,你开始质疑,他们在想什么。

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

DC