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

dsi*_*cha 367 java unsigned integer language-design

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

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

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

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

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

Uri*_*Uri 190

这是来自Gosling和其他人对简单性的采访:

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

  • 我将不得不在这里与一个具体的例子(来自CLR)不同意Gosling.给阵列一个有符号整数长度值或无符号长度更令人困惑的是什么?数组不可能有负长度,但我们的API表明这是可能的. (220认同)
  • 我更喜欢简单的代码简洁性.这就是我讨厌Java的原因. (82认同)
  • 好吧,他刚刚讲述了没有无符号类型的优点.现在让我们来看看它们的缺点...... (81认同)
  • 如果Java需要无符号整数,因为数组索引不能为负数,那么它也需要子范围(la Pascal),因为数组索引不能大于数组大小. (59认同)
  • 让Java变得简单的论点是让我们陷入困境的一部分,因为缺少模板,他们最终将这些模板带入语言,因为替代方案非常繁琐.我确实认为可以使用适当的类支持无符号整数,但它不需要prims (18认同)
  • 作为一名开发人员,我发现侮辱Gosling认为我不够聪明,无法理解有符号和无符号整数.Java仅适用于初学者吗? (15认同)
  • @Uri,如果你做数学密集型工作,这可能是个大问题.考虑原始类型的JITing.关于基元的数学指令可以很容易地JIT到相应的程序集中.但如果数学是手动实现的,它将不会直接转换为装配,因此性能将受到影响. (8认同)
  • 我想这个家伙并没有意识到写一个二进制文件可能会导致很多痛苦,因为有人只有128作为一个字节的最高值. (7认同)
  • 戈斯林,高兴地说.这不是Ask工具栏,是Java最深的耻辱. (7认同)
  • @Uri是的,但它几乎肯定不会像JVM中的原始实现一样高效. (6认同)
  • 作为开发人员,我会说一种语言永远不会决定一个人编码的方式,它应该提供设施并将其用于开发人员.他只是通过为负长度的阵列提供设施来创建一个非常好的角落案例 (5认同)
  • 对于某些事情,IMOH Java可能比C++更简单,但也有一些丑陋的东西.许多库类有些复杂(例如日期和日历).并考虑一个有趣的事实,即AA(AA)`是一个有效的方法定义:D (4认同)
  • 在最后一个Javapolis(在它成为Devoxx之前)有一段Gosling的故事讲述了这个故事.Bloch指出,给出的示例对签名的int也有效.戈斯林被整体搞糊涂了.明智的语言应该只适用于任意大小的整数. (3认同)
  • @Pius:更糟糕的是语言简单化,其中某些规则适用于所有上下文的要求迫使选择次优规则.*only*原因我可以看到允许隐式float转换为双转换而不是反之亦然,这是为了避免在应用于混合浮点数和双精度时将`==`作为等价运算符以外的其他函数工作; 我认为在这种情况下避免让`==`作为等价运算符之外的其他东西的更好的方法是禁止直接`float` -`double`比较,这几乎总是错误的*. (3认同)
  • 对任何开发人员进行有关浮点数的测验,很快您就会发现许多开发人员实际上并不完全理解浮点运算的原理……说实话,我开始对 Java 哲学感到非常恼火 (3认同)
  • @JaredPar:嗯,现代(JIT)编译器非常聪明,可以优化很多.如果猜测在很多情况下可以优化一个简单的Unsigned类,直到无符号整数的本机CPU操作.所以我不认为它绝对必须是原始的.事实上,许多人认为原始(不再)真的是必要的.Smalltalk例如没有原语,只有对象. (2认同)
  • @Uri我认为泛型(不是模板)应该是Java,只是让规范(和实现)正确是困难的.有些人甚至认为应该花更多的时间. (2认同)
  • @JaredPar如果你希望类型实际上像这样约束语义,我认为Java缺乏总和类型和新类型是正确性的更大障碍。 (2认同)
  • 将java与允许和理解`unsigned long long`的语言进行比较是不公平的,Java可以使它们的`=='工作,到目前为止它只比PHP的`==`稍微好一点,IMO,Java已经过时了所有这些过于简单化,C#每天只会扩展和销毁java.现在这样一种羞耻的语言不知道`unsigned`,因为它声称"简单"有超过800页的lang规范,而C#允许无符号和指针有511页的lang规范.这种"简单"语言比C语言更复杂.但是拒绝使用简单的"unsigned" (2认同)
  • @Felype:我不介意高斯林决定禁止其值不能全部表示为“int”的无符号类型。我不认为他的理由足以证明缺乏无符号 8 位类型,也没有充分证明缺乏无符号 16 位类型(其目的是存储数字),因为无符号字节类型与“int”的交互不会更糟与带有“int”的有符号字节类型相比:要么提升为“int”。 (2认同)
  • @supercat当你在Java中将“byte”提升为“int”时,你必然会得到一个负结果,因此你需要将值修剪到最低8位,例如。`int n = (b[0] & 0xFF);`。**对于无符号类型,不会发生符号扩展。** (2认同)

Nei*_*fey 50

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

  • 通常,Java设计者希望简化可用数据类型的所有组成部分
  • 对于日常用途,他们认为最常见的需求是签名数据类型
  • 为了实现某些算法,有时需要无符号算法,但是那种将要实现这种算法的程序员也可以通过签名数据类型"循环"执行无符号算法.

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

  • 使得字节无符号,或者至少为这一种数据类型提供了有符号/无符号的备选方案(可能有不同的名称)(使其签名有利于一致性,但何时需要有符号的字节?)
  • 完成了'短'(你最近什么时候使用16位带符号算术?)

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

  • "对于日常用途,他们认为最常见的需求是签名数据类型".在我的C++代码中,我经常发现自己在想"为什么我在这里使用有符号整数而不是无符号整数?!".我觉得"签名"是例外而不是规则(当然,它取决于域名,但有正当整数被称为*自然*数字的原因;-)). (63认同)
  • 拇指向上调用无符号字节,在进行图像处理时,假设字节是无符号的(应该是这样),让我花了几个小时调试. (15认同)
  • 虽然在特定的应用程序中,你应该*测量*是否使用短裤可以提供更好的性能,而不是假设它是真的.操纵短路而不是整数(通常是处理器喜欢使用的类型)所需的额外jiggery-pokery可能实际上对特定应用中的性能有害.并非总是如此,但你应该测试,而不是假设. (8认同)
  • 你会惊讶地发现`short`的使用频率是多少 - defltate/gzip/inflate算法都是16bit而且它们严重依赖于短路......或者至少是`short []`[不可否认它们是原生的 - 但算法的java impl仍然存在数据的数据].后者(`short []`)对`int []`有明显的优势,因为它占用的内存少两倍,内存少=缓存属性更好,性能更好. (7认同)
  • 我也希望有无符号字节,但我怀疑整数类型之间完全一致性的优势超过了无符号字节带来的便利. (2认同)
  • 谎言 - 你说的是真的,但到目前为止,64位处理器仍然给32位宽度带来某种"特权"状态.如果我们确实转向例如128位处理器,那么看看32位是否开始变得更加过时将会很有趣. (2认同)
  • 128位和256位处理器在很多年里都不会流行.Java(希望)早已不复存在. (2认同)

Jyr*_*117 18

这是一个较老的问题,拍了很简单地提到了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--;
Run Code Online (Sandbox Code Playgroud)

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


编辑

对于JDK8,有一些新的API Long,Integer它们在处理longint值时提供辅助方法作为无符号值.

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

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

  • 这可能是Java的缺点 (3认同)
  • 但是,例如,char太小而不能支持long算法。 (2认同)
  • 当我尝试从硬盘驱动器读取数据时,我遇到了这个问题,该数据是由 C 程序写入的,该程序具有 C 结构的规范。我不仅被迫处理 Endian 差异,而且雪上加霜的是,使用 64 位整数读取所有内容,然后应用移位操作来获取正确的数据,只是因为有人决定他们不想实现无符号数字。抱歉,如果他们被允许说“为了保持 java 简单”之类的话,那么我会说这是一个懒惰的原因。 (2认同)

小智 15

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

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

  • 是的,我确信Gosling与你相比对Java很无知. (37认同)
  • @starblue当然是,但这是一个克服语言限制的黑客 (5认同)
  • 使用`char`除了字符之外的任何东西都是不好的风格. (2认同)

Bom*_*mbe 14

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

  • 至于混合有符号/无符号:您可以使用无符号类型,但不允许混合(或要求显式强制转换).不过,还不清楚是否有必要. (12认同)
  • 当你在Java中遇到图像处理问题时,请看看我,你希望字节是无符号的.然后你就会知道`&0xFF`'每个字节到int的提升都会使代码变得更加混乱. (12认同)
  • 第8位在那里,它只是试图隐藏自己作为标志. (4认同)
  • 在C++中,你必须在`static_cast`s周围撒上很多东西来混合它们.这确实是凌乱的. (2认同)

aka*_*tos 12

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

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

  • Java签名的整数也是如此.我不明白你的观点. (34认同)
  • @foo:签名整数必须在它们引起问题之前变大.相比之下,在C中,任何负整数 - 甚至是"-1" - 与任何无符号数 - 甚至零相比都会有问题. (8认同)
  • 不要咆哮你的答案,但将"-1"作为"未知"年龄(正如文章建议的那样)是"代码嗅觉"的**经典例子之一**.例如,如果你想计算"爱丽丝比鲍勃多大多少?",A = 25和B = -1,你会得到一个"±26"的答案,这是完全错误的.正确处理未知值是某种`Option <TArg>`当`Some(25) - None`会返回`None`. (4认同)

sta*_*lue 11

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

至于实用建议:

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

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

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

又见这个回答有关"移植一个随机数发生器从C到Java的".

  • "......即使使用简化模型,大多数Java程序员也不知道基本数字类型的行为......"我的某些东西只是谴责一种针对最低公分母的语言. (7认同)
  • ......并且转移. (5认同)
  • 是的,对于右移,你必须分别在有符号和无符号的情况下选择`>>`和`>>>`.左移是没问题的. (5认同)

Joh*_*all 6

使用JDK8,它确实有一些支持.

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

  • 又名"所以人们真的使用它,我们错误地不把它包括在内" - 但我们仍然不太相信Java开发人员知道变量是否签名 - 所以我们不打算实现它们在VM中或作为与其签名堂兄相当的类型. (12认同)

Mor*_*Adi 6

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


小智 5

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


Jon*_*han 5

我曾经和 C++ 标准委员会的某个人一起参加过 C++ 课程,他暗示 Java 做出了正确的决定来避免使用无符号整数,因为 (1) 大多数使用无符号整数的程序可以很好地处理有符号整数,这在人们的思维方式,以及 (2) 使用无符号整数会导致许多易于创建但难以调试的问题,例如整数算术溢出和在有符号和无符号类型之间转换时丢失重要位。如果您错误地使用有符号整数从 0 中减去 1,则通常会更快地导致您的程序崩溃,并且比使用 2^32 - 1 更容易找到错误,并且编译器和静态分析工具以及运行时检查必须假设您知道自己在做什么,因为您选择使用无符号算术。还,

很久以前,当内存有限并且处理器不能一次自动在 64 位上运行时,每一位都算得上更多,因此有符号与无符号字节或短字节实际上更重要,而且显然是正确的设计决策。今天,在几乎所有常规编程情况下,仅使用有符号 int 就足够了,如果您的程序确实需要使用大于 2^31 - 1 的值,那么无论如何您通常只想要一个 long。一旦您进入了使用 long 的领域,就更难想出一个原因,为什么您真的无法使用 2^63 - 1 个正整数。每当我们使用 128 位处理器时,问题就更小了。