为什么Java不支持无符号整数?
-
08-07-2019 - |
题
为什么 Java 不支持无符号整数?
在我看来,这是一个奇怪的遗漏,因为它们允许人们编写不太可能在意外大的输入上产生溢出的代码。
此外,使用无符号整数可以是自文档的一种形式,因为它们表明无符号 int 想要保存的值永远不应该是负数。
最后,在某些情况下,无符号整数对于某些运算(例如除法)可能更有效。
包括这些有什么缺点?
解决方案
这是关于简单性的采访Gosling及其他人 :
戈斯林:对我来说,作为一名语言设计师,我现在并不认为自己是“简单”的。真的最终意义是我可以期待J.随机开发人员在他的头脑中保持规格。这个定义说,例如,Java不是 - 实际上很多这些语言都有很多极端情况,这些都是没人真正理解的。测试任何C开发人员关于unsigned的问题,很快你就会发现几乎没有C开发人员真正理解无符号算法是什么,无符号算术是什么。这样的事情让C变得复杂。我认为Java的语言部分非常简单。你必须查找的库。
其他提示
从字里行间看,我认为逻辑是这样的:
- 一般来说,Java 设计者希望简化可用数据类型的全部内容
- 对于日常用途,他们认为最常见的需求是签名数据类型
- 为了实现某些算法,有时需要无符号算术,但是将实现此类算法的程序员也将具备“解决”使用有符号数据类型进行无符号算术的知识
大多数情况下,我会说这是一个合理的决定。可能,我会:
- 使字节无符号,或者至少为这种数据类型提供了有符号/无符号替代方案,可能具有不同的名称(使其有符号有利于一致性,但什么时候需要有符号字节?)
- 取消“short”(您上次使用 16 位有符号算术是什么时候?)
尽管如此,通过一些拼凑,对高达 32 位的无符号值进行运算并不算太糟糕,而且大多数人不需要无符号 64 位除法或比较。
这是一个较旧的问题,帕特确实简短地提到了 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 中提供了新的 API Long
和 Integer
在治疗时提供辅助方法 long
和 int
值作为无符号值。
compareUnsigned
divideUnsigned
parseUnsignedInt
parseUnsignedLong
remainderUnsigned
toUnsignedLong
toUnsignedString
此外, 番石榴 提供了许多帮助器方法来对整数类型执行类似的操作,这有助于缩小由于缺乏对整数类型的本机支持而留下的空白 unsigned
整数。
Java确实有无符号类型,或者至少有一个:char是unsigned short。因此无论戈斯林抛出什么理由,他的无知真的只是为什么没有其他无符号类型。
短片类型:短片一直用于多媒体。原因是您可以在一个32位无符号长度中拟合2个样本并对多个操作进行向量化。与8位数据和无符号字节相同。您可以在寄存器中放入4或8个样本进行矢量化。
只要签名和未签名的整数混合在一个表达式中,事情就会开始变得混乱,你可能将丢失信息。将Java限制为签名的内容只能真正解决问题。我很高兴我不必担心整个签名/未签名的业务,尽管我有时会错过一个字节中的第8位。
http://skeletoncoder.blogspot.com/ 2006/09 / java的教程-为什么-NO-unsigned.html
这个人说因为C标准定义了涉及无符号和有符号整数的操作被视为无符号。这可能会导致负的有符号整数转换为大的unsigned int,从而可能导致错误。
我认为 Java 本身就很好,添加 unsigned 会使它变得复杂而没有太多好处。即使使用简化的整数模型,大多数 Java 程序员也不知道基本数字类型的行为方式 - 只需阅读本书即可 Java 谜题 看看你可能持有哪些误解。
至于实用建议:
如果您的值有点任意大小并且不适合
int
, , 使用long
。如果他们不适合long
使用BigInteger
.仅当需要节省空间时才对数组使用较小的类型。
如果您正好需要 64/32/16/8 位,请使用
long
/int
/short
/byte
除了除法、比较、右移和转换之外,不再担心符号位。
也可以看看 这 回答“将随机数生成器从 C 移植到 Java”。
使用 JDK8 ,它确实为他们提供了一些支持。
尽管有Gosling的担忧,我们仍然可以看到Java中对无符号类型的完全支持。
我知道这篇文章太旧了;但是为了您的兴趣,在Java 8及更高版本中,您可以使用 int
数据类型来表示无符号的32位整数,其最小值为0,最大值为2 32 − 1。使用 Integer
类将 int
数据类型用作无符号整数和静态方法,如 compareUnsigned()
, divideUnsigned()代码>等已添加到
Integer
类中,以支持无符号整数的算术运算。
我听说过它们将被包含在原始Java版本附近。 Oak是Java的前身,在一些规范文档中提到了使用的值。不幸的是,这些从未成为Java语言。至于任何人都能够弄清楚他们只是没有得到实施,可能是由于时间限制。
我曾经和C ++标准委员会的某个人一起学过C ++课程,他暗示Java做出了正确的决定,避免使用无符号整数,因为(1)大多数使用无符号整数的程序都可以用有符号整数做同样的事情,这是在人们如何思考方面更自然,并且(2)使用无符号整数导致很多容易创建但很难调整诸如整数算术溢出和在有符号和无符号类型之间转换时丢失有效位的问题。如果你错误地使用有符号整数从0中减去1,它通常会更快地导致程序崩溃,并且比查找错误更容易找到错误,并且编译器和静态分析工具以及运行时检查必须假设你知道自从你选择使用无符号算术后你正在做什么。此外,像-1这样的负数通常代表一些有用的东西,比如一个字段被忽略/默认/未设置,而如果你使用无符号,你必须保留一个特殊的值,如2 ^ 32 - 1或类似的东西。
很久以前,当内存有限且处理器不能同时自动运行64位时,每一位都计算得更多,因此签名与无符号字节或短路实际上更为重要,显然是正确的设计决策。今天只使用带符号的int在几乎所有常规编程案例中都是绰绰有余的,如果你的程序确实需要使用大于2 ^ 31 - 1的值,那么你通常只需要很长的时间。一旦你进入使用多头的领域,你就更难想出为什么你真的无法通过2 ^ 63-1正整数。每当我们使用128位处理器时,它都会成为一个问题。
您的问题是“为什么Java不支持无符号整数”?
我对你的问题的回答是,Java希望所有这些都是原始类型: byte , char , short , int 和 long 应被视为 byte , word , dword 和 qword 分别与汇编完全相同,Java运算符在 char 之外的所有原始类型上都是签名操作,但仅限于 char 他们只有16位无符号。
因此静态方法假设32位和64位的无符号操作也。
您需要最终类,可以为无符号操作调用其静态方法。
您可以创建此最终类,将其命名为您想要的任何名称,并实现它的静态方法。
如果您不知道如何实施静态方法,请参阅链接可以帮到你。
在我看来,如果 既不支持无符号类型也不,那么Java 不类似于C ++ >运算符重载,所以我认为Java应该被视为与C ++和C语言完全不同的语言。
顺便说一句,语言的名称也完全不同。
所以我不建议在Java中键入类似于C的代码,我不建议输入类似于C ++的代码,因为在Java中你将无法做你想做的事情在C ++中,即代码不会继续像C ++一样,对我来说这样的代码很糟糕,改变中间的风格。
我建议为签名操作编写和使用静态方法,因此除了在代码中只需要签名操作外,你不会在操作符和静态方法的代码混合中看到有符号和无符号操作,可以仅使用运算符。
我还建议避免使用短, int 和 long 基元类型,并使用 word ,<相反,分别是strong> dword 和 qword ,您将调用静态方法进行无符号运算和/或签名运算,而不是使用运算符。
如果您只打算执行签名操作并仅在代码中使用运算符,那么可以使用这些原始类型 short , int 和长强>
实际上字, dword 和 qword 在语言中存在不,但您可以创建每个新类和每个的实现应该很容易:
类单词仅包含基本类型短,类 dword 仅包含基本类型 int 并且 qword 类仅包含基本类型 long 。现在所有无符号和已签名的方法都是静态或不是您的选择,您可以在每个类中实现,即所有16位操作都是无符号的,并通过在 word 类上给出含义名称来签名,所有32位操作既未签名又通过在 dword 类上给出含义名称进行签名,所有64位操作都是无符号的,并通过在 qword 类上赋予含义名称进行签名。 / p>
如果你不喜欢为每个方法提供太多不同的名称,你总是可以在Java中使用重载,很高兴看到Java 不也删除了它!
如果你想要方法而不是8 bi的运算符
因为 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 ...
}
你注意到了这个错误吗?我承认我只是在踩到调试器后才看到它。
因为 n
是无符号类型 size_t
,所以整个表达式 n - (rays.size() - 1)/ 2
的计算结果为<代码>无符号代码>。该表达式旨在成为中间的 n
光线的签名位置:左侧中间的第1条光线将具有位置-1,右边的第一个将具有位置+1等。在获取abs值并乘以 delta
角度之后,我将获得 n
th ray和中间之间的角度之一。
不幸的是,对于我来说,上面的表达式包含了邪恶的无符号,而不是评估,比如-1,它被评估为2 ^ 32-1。随后转换为 double
密封了该错误。
在误用 unsigned
算法导致一两个错误之后,必须开始想知道额外的一个比特是否值得额外的麻烦。我尽可能地尝试避免在算术中使用 unsigned
类型,尽管仍然将它用于非算术运算,例如二进制掩码。
“C”规范中有一些宝石出于实用原因而放弃了Java,但随着开发人员的需求(封闭等)逐渐消失。
我提到了第一个,因为它与这个讨论有关;指针值遵循无符号整数运算。并且,与此线程主题相关,难以在Java的签名世界中维护无符号语义。
我猜如果有人要让丹尼斯·里奇改变自我,告诉戈斯林的设计团队,它会建议给予签名的“无穷大零”,以便所有地址抵消请求首先添加他们的代数环大小以避免负值。
这样,在数组中抛出的任何偏移都不会生成SEGFAULT。例如,在封装的类中,我称之为双重的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永远不会从负面索引中“获得”。请记住,还有许多合法的请求要求先前(负)索引值。
注意:外部%模数取消引用合法请求,而内部%模数掩盖了比负模式更负面的明显恶意。如果这出现在Java + .. + 9 ||中8 + .. +规范,然后问题将真正成为一个“不能”自我旋转的“程序员”。 FAULT”。
我确信所谓的Java unsigned int'infect'可以用上面的单行补偿。
PS:只是为上面的RingArray内务管理提供上下文,这里是一个候选'set'操作来匹配上面的'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
}
我能想到一个不幸的副作用。在java嵌入式数据库中,32位id字段可以拥有的id数量是2 ^ 31,而不是2 ^ 32(约20亿,不是~40亿)。
恕我直言的原因是因为他们懒得实施/纠正这个错误。 建议C / C ++程序员不理解无符号,结构,联合,位标志......只是荒谬。
Ether,你正在和一个基本的/ bash / java程序员谈论,开始编程la C,没有任何真正的知识这种语言,或者你只是出于自己的想法。 ;)
当你每天从文件或硬件上处理你开始质疑的格式时,他们到底在想什么。
这里的一个很好的例子是尝试使用无符号字节作为自旋循环。 对于那些不理解最后一句的人,你怎么称自己为程序员。
DC