为什么Java不支持无符号整数?
在我看来,这是一个奇怪的遗漏,因为它们允许人们编写不太可能在意外的大输入上产生溢出的代码。
此外,使用无符号整数可以是一种自我文档形式,因为它们表明unsigned int意图保留的值绝不应该是负数。
最后,在某些情况下,无符号整数对于某些操作(例如除法)可能更有效。
包含这些内容的不利之处是什么?
答案 0 :(得分:184)
这来自interview with Gosling and others,关于简单性:
戈斯林:作为一名语言设计师,对于我来说,我现在并不像以前那样,“简单”到底意味着什么,我可以期待J. Random Developer能够掌握这个规范。这个定义说,例如,Java不是 - 实际上很多这些语言都有很多极端情况,这些都是没人真正理解的。测试任何C开发人员关于unsigned的问题,很快你就会发现几乎没有C开发人员真正理解无符号算法是什么,无符号算术是什么。这样的事情让C变得复杂。我认为Java的语言部分非常简单。你必须查找的库。
答案 1 :(得分:50)
在线之间阅读,我认为逻辑是这样的:
大多数情况下,我认为这是一个合理的决定。可能,我会:
尽管如此,对于高达32位的无符号值的操作并不太糟糕,并且大多数人不需要无符号的64位除法或比较。
答案 2 :(得分: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--;
是的,没有对无符号整数的直接支持(显然,如果有直接支持,我不需要将大部分操作转换回char)。但是,肯定存在无符号原始数据类型。我也希望看到一个无符号字节,但我猜想将内存成本加倍,而使用char是一个可行的选择。
使用JDK8,有Long
和Integer
的新API,在将long
和int
值视为无符号值时提供帮助方法。
compareUnsigned
divideUnsigned
parseUnsignedInt
parseUnsignedLong
remainderUnsigned
toUnsignedLong
toUnsignedString
此外,Guava提供了许多辅助方法来对整数类型执行类似的操作,这有助于缩小由于缺少对unsigned
整数的本机支持而留下的空白。
答案 3 :(得分:16)
Java确实有无符号类型,或者至少有一个:char是unsigned short。因此无论戈斯林抛出什么理由,他的无知真的只是为什么没有其他无符号类型。
短片类型:短片一直用于多媒体。原因是您可以在一个32位无符号长度中拟合2个样本并对多个操作进行向量化。与8位数据和无符号字节相同。您可以在寄存器中放入4或8个样本进行矢量化。
答案 4 :(得分:14)
只要签名和未签名的整数混合在一个表达式中,事情就会开始变得混乱,你可能将丢失信息。将Java限制为签名的内容只能真正解决问题。我很高兴我不必担心整个签名/未签名的业务,尽管我有时会错过一个字节中的第8位。
答案 5 :(得分:12)
http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html
这个人说因为C标准定义了涉及无符号和有符号整数的操作被视为无符号。这可能会导致负的有符号整数转换为大的unsigned int,从而可能导致错误。
答案 6 :(得分:11)
我认为Java很好,添加unsigned会使它复杂化而没有太大的收获。 即使使用简化的整数模型,大多数Java程序员也不知道基本数字类型的行为 - 只需阅读书籍Java Puzzlers即可了解您可能持有的误解。
至于实用建议:
如果您的值有些任意且不适合int
,请使用long
。
如果它们不适合long
使用BigInteger
。
当您需要节省空间时,仅将较小的类型用于数组。
如果您需要64/32/16/8位,请使用long
/ int
/ short
/ byte
并停止担心符号位,除了分工,比较,右移和铸造。
另见this关于“将随机数生成器从C移植到Java”的答案。
答案 7 :(得分:6)
使用JDK8,它确实有一些支持。
尽管有Gosling的担忧,我们仍然可以看到Java中对无符号类型的完全支持。
答案 8 :(得分:6)
我知道这篇文章太旧了;但是为了您的兴趣,在Java 8及更高版本中,您可以使用int
数据类型来表示无符号的32位整数,其最小值为0,最大值为2 32 -1。使用Integer
类将int
数据类型用作无符号整数,并将compareUnsigned()
,divideUnsigned()
等静态方法添加到Integer
类中支持无符号整数的算术运算。
答案 9 :(得分:4)
我听说过它们将被包含在原始Java版本附近。 Oak是Java的前身,在一些规范文档中提到了使用的值。不幸的是,这些从未成为Java语言。至于任何人都能够弄清楚他们只是没有得到实施,可能是由于时间限制。
答案 10 :(得分:3)
我曾经和C ++标准委员会的某个人一起学过C ++课程,他暗示Java做出了正确的决定,避免使用无符号整数,因为(1)大多数使用无符号整数的程序都可以用有符号整数做同样的事情,这是在人们如何思考方面更自然,并且(2)使用无符号整数导致很多容易创建但很难调整诸如整数算术溢出和在有符号和无符号类型之间转换时丢失有效位的问题。如果你错误地使用有符号整数从0中减去1,它通常会更快地导致程序崩溃,并且比查找错误更容易找到错误,并且编译器和静态分析工具以及运行时检查必须假设你知道自从你选择使用无符号算术后你正在做什么。此外,像-1这样的负数通常代表一些有用的东西,比如一个字段被忽略/默认/未设置,而如果你使用无符号,你必须保留一个特殊的值,如2 ^ 32 - 1或类似的东西。
很久以前,当内存有限且处理器不能同时自动运行64位时,每一位都计算得更多,因此签名与无符号字节或短路实际上更为重要,显然是正确的设计决策。今天只使用带符号的int在几乎所有常规编程案例中都是绰绰有余的,如果你的程序确实需要使用大于2 ^ 31 - 1的值,那么你通常只需要很长的时间。一旦你进入使用多头的领域,你就更难想出为什么你真的无法通过2 ^ 63-1正整数。每当我们使用128位处理器时,它都会成为一个问题。
答案 11 :(得分:2)
你的问题是"为什么Java不支持无符号的整数"?
我对你的问题的回答是,Java希望所有这些原始类型:字节,字符,短 , int 和 long 应视为字节,字, dword 和< strong> qword 分别与汇编完全相同,Java运算符对 char 以外的所有原始类型进行签名操作,但是仅在 char 上,它们仅为16位无符号。
因此静态方法假设32位和64位的无符号操作也。
您需要最终类,可以为无符号操作调用其静态方法。
您可以创建此最终类,将其命名为您想要的任何名称并实现它的静态方法。
如果你不知道如何实现静态方法,那么link可以帮助你。
在我看来,如果 既不支持无符号类型也不,那么Java 不类似于C ++ >运算符重载,所以我认为Java应该被视为与C ++和C语言完全不同的语言。
顺便说一句,语言的名称也完全不同。
所以我不建议使用Java键入类似于C的代码,我不建议输入类似于C ++的代码,因为在Java中你无法做到你想在C ++中做什么,即代码不会继续像C ++一样,对我来说这样做是不好的,改变中间的风格。
我建议也为已签名的操作编写和使用静态方法,因此除非在代码中只需要签名操作,否则您不会在运算符和静态方法的代码混合中看到有符号和无符号操作。 ,只使用运营商是可以的。
我还建议避免使用短, int 和 long 基元类型,并使用 word ,<相反,分别是strong> dword 和 qword ,您将调用静态方法进行无符号运算和/或签名运算,而不是使用运算符。
如果您只打算执行签名操作并仅在代码中使用运算符,那么可以使用这些原始类型 short , int 和长强>
实际上字, dword 和 qword 在语言中存在 n&#t; ,但是你可以为每个创建新类,每个的实现应该非常简单:
类单词仅包含基本类型短,类 dword 仅包含基本类型 int 并且 qword 类仅包含基本类型 long 。现在所有无符号和已签名的方法都是静态或不是您的选择,您可以在每个类中实现,即所有16位操作都是无符号的,并通过在 word 类上给出含义名称来签名,所有32位操作既未签名又通过在 dword 类上给出含义名称进行签名,所有64位操作都是无符号的,并通过在 qword 类上赋予含义名称进行签名。 / p>
如果你不想为每种方法提供太多不同的名称,你总是可以在Java中使用重载,那么阅读Java确实 n&#t; t 也删除它!
如果您想要8位有符号操作的方法而不是运算符,而对于根本没有运算符的8位无符号运算,则可以创建 Byte 类(请注意第一个字母& #39; B&#39;是资本,所以这不是原始类型 byte )并实现此类中的方法。
关于按值传递并通过引用传递:
如果我没有错,就像在C#中一样,原始对象自然地通过值传递,但类对象自然地通过引用传递,因此这意味着字节,字类型的对象, dword 和 qword 将通过引用传递,而不是默认值。我希望Java有像C#那样的 struct 对象, 所以字节,字, dword 和 qword 都可以实现 struct 而不是类,所以默认情况下它们是按值传递的,而不是默认情况下通过引用传递,就像C#中的任何结构对象一样,默认情况下通过值传递,而不是通过引用传递,但是因为Java比C#差,我们必须处理它,然后只有类和接口,它们通过引用传递,而不是默认值。因此,如果您想按值传递字节,字, dword 和 qword 对象,而不是通过引用传递,例如在Java和C#中的任何其他类对象中,您只需使用复制构造函数即可。
这是我能想到的唯一解决方案。我只希望我可以将原始类型定义为word,dword和qword,但Java既不支持typedef也不支持使用,不像支持使用的C#,这相当于C&#39; s typedef。
关于输出:
对于相同的位序列,您可以通过多种方式打印它们:二进制,十进制(如C printf中%u的含义),八进制(如%的含义) o在C printf中),十六进制(如C printf中%x的含义)和整数(如C printf中%d的含义)。
请注意,C printf不知道作为参数传递给函数的变量的类型,因此printf只知道传递给函数第一个参数的char *对象的每个变量的类型。 / p>
所以在每个类中:字节,字, dword 和 qword ,您可以实现打印方法并获得printf的功能,即使该类的基本类型已签名,您仍然可以通过遵循一些涉及逻辑和移位操作的算法将其打印为无符号,以将数字打印到输出。
不幸的是,我给你的链接并没有显示如何实现这些打印方法,但我相信你可以谷歌搜索实现这些打印方法所需的算法。
我可以回答你的问题并建议你。
答案 12 :(得分:1)
因为#redirect old URL to new SEO
RewriteCond %{THE_REQUEST} ^GET\ /+tag\.php\?t=([^&\s]+)&page=1
RewriteRule ^ /tags/%1/? [R=301,L]
#redirect old URL to new SEO
RewriteCond %{THE_REQUEST} ^GET\ /+tag\.php\?t=([^&\s]+)&page=([^&\s]+)
RewriteRule ^ /tags/%1/page-%2/? [R=301,L]
#redirect old URL to new SEO
RewriteCond %{THE_REQUEST} ^GET\ /+category\.php\?cat=([^&\s]+)&page=([^&\s]+)&sortby=([^&\s]+)
RewriteRule ^ /browse-%1-videos-%2-%3.html? [R=301,L]
#redirect old URL to new SEO
RewriteCond %{THE_REQUEST} ^GET\ /+category\.php\?cat=([^&\s]+)
RewriteRule ^ /browse-%1-videos.html? [R=301,L]
RewriteRule ^tags/([^/]+)/$ tag.php?t=$1&page=1 [L]
RewriteRule ^tags/([^/]+)/page-([0-9]+)(/)?$ tag.php?t=$1&page=$2 [L]
RewriteRule ^browse-(.*)-videos.html$ category.php?cat=$1 [L]
RewriteRule ^browse-(.*)-videos-([0-9]+)-(.*).html$ category.php?cat=$1&page=$2&sortby=$3 [L]
类型纯粹是邪恶的。
C unsigned
产生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 ...
}
是无符号类型n
,因此整个表达式size_t
的计算结果为n - (rays.size() - 1) / 2
。该表达式旨在成为来自中间的unsigned
光线的签名位置:左侧中间的第一条光线将具有位置-1,第一条光线位于第一条光线中在右边将有位置+1等。在获取abs值并乘以n
角度后,我将获得delta
光线与中间光线之间的角度。
不幸的是,对于我来说,上面的表达式包含了邪恶的无符号,而不是评估,比如-1,它被评估为2 ^ 32-1。随后转换为n
可以阻止该错误。
在误用double
算术引起的一两个错误之后,必须开始想知道额外的比特是否值得额外的麻烦。我尽可能地尝试避免在算术中使用unsigned
类型,尽管仍然将它用于非算术运算,例如二进制掩码。
答案 13 :(得分:0)
'C'规范中有一些宝石是Java出于实用原因而放弃的,但随着开发人员的需求(关闭等)而逐渐回落。
我提到第一个,因为它与本次讨论有关;指针值是否符合无符号整数算法。并且,与该线程主题相关,在Java的Signed世界中维护Unsigned语义存在困难。
我想如果要让Dennis Ritchie改变自我来建议Gosling的设计团队,那就建议给Signed的“无穷大为零”,这样所有地址偏移量请求都将首先添加其ALGEBRAIC RING SIZE来消除负数值。
这样,在数组上抛出的任何偏移都永远不会生成SEGFAULT。例如,在一个封装的类中,我将其称为需要无符号行为的double的RingArray-在“自旋转循环”上下文中:
// ...
// Housekeeping state variable
long entrycount; // A sequence number
int cycle; // Number of loops cycled
int size; // Active size of the array because size<modulus during cycle 0
int modulus; // Maximal size of the array
// Ring state variables
private int head; // The 'head' of the Ring
private int tail; // The ring iterator 'cursor'
// tail may get the current cursor position
// and head gets the old tail value
// there are other semantic variations possible
// The Array state variable
double [] darray; // The array of doubles
// somewhere in constructor
public RingArray(int modulus) {
super();
this.modulus = modulus;
tail = head = cycle = 0;
darray = new double[modulus];
// ...
}
// ...
double getElementAt(int offset){
return darray[(tail+modulus+offset%modulus)%modulus];
}
// remember, the above is treating steady-state where size==modulus
// ...
以上的RingArray永远不会从负索引中“获取”,即使恶意请求者试图这样做也是如此。请记住,也有许多合法的要求来请求先前的(负)索引值。
NB:外部%modulus取消引用合法请求,而内部%modulus从比-modulus更负面的负面信息中掩盖公然恶意。如果要在Java + .. + 9 ||中出现8 + .. +规格,那么问题就会真正变成“无法“自转” FAULT的程序员”。
我敢肯定,上述的单行代码可以弥补所谓的Java unsigned int'deficiency'。
PS:只是为了给上面的RingArray内务处理提供上下文,这是一个与上面的“ get”元素操作相匹配的候选“设置”操作:
void addElement(long entrycount,double value){ // to be called only by the keeper of entrycount
this.entrycount= entrycount;
cycle = (int)entrycount/modulus;
if(cycle==0){ // start-up is when the ring is being populated the first time around
size = (int)entrycount; // during start-up, size is less than modulus so use modulo size arithmetic
tail = (int)entrycount%size; // during start-up
}
else {
size = modulus;
head = tail;
tail = (int)entrycount%modulus; // after start-up
}
darray[head] = value; // always overwrite old tail
}
答案 14 :(得分:-2)
我能想到一个不幸的副作用。在java嵌入式数据库中,32位id字段可以拥有的id数量是2 ^ 31,而不是2 ^ 32(约20亿,不是〜40亿)。
答案 15 :(得分:-7)
恕我直言的原因是因为他们懒得实施/纠正这个错误。 建议C / C ++程序员不理解无符号,结构,联合,位标志......只是荒谬。
Ether,你正在和一个基本/ bash / java程序员谈论,开始编程la C,没有任何真正的知识这种语言,或者你只是出于自己的想法。 ;)
当你每天从文件或硬件上处理你开始质疑的格式时,他们到底在想什么。
这里的一个很好的例子是尝试使用无符号字节作为自旋循环。 对于那些不理解最后一句的人,你怎么称自己为程序员。
DC