Javaが符号なしintをサポートしないのはなぜですか?
-
08-07-2019 - |
質問
Javaに符号なし整数のサポートが含まれないのはなぜですか?
予想外に大きな入力でオーバーフローが発生する可能性が低いコードを作成できるため、奇妙な省略に思えます。
さらに、符号なし整数の使用は、自己署名の形式になります。これは、符号なし整数が保持する予定の値が負になることはないことを示すためです。
最後に、場合によっては、除算などの特定の操作に対して符号なし整数の方が効率的です。
これらを含めることのマイナス面は何ですか?
解決
これは、ゴスリングなどとのインタビューからのものです。 >
Gosling:言語デザイナーとしての私にとって、私は最近自分自身を「シンプル」なものとして数えていません。本当に意味があったのは、J。Random Developerが彼の頭の中で仕様を保持することを期待できるということでした。その定義によれば、たとえば、Javaはそうではありません。実際、これらの言語の多くは、多くのコーナーケースを抱えています。符号なしのC開発者をクイズすると、符号なしで何が起こっているのか、符号なし算術とは何かを実際に理解しているC開発者はほとんどいないことがすぐにわかります。そのようなことがCを複雑にしました。 Javaの言語部分は非常に単純だと思います。検索する必要のあるライブラリ。
他のヒント
行を読んで、ロジックは次のようなものだったと思います:
- 一般に、Javaデザイナーは利用可能なデータ型のレパートリーを単純化したかったのです
- 日常的な目的のために、彼らは最も一般的なニーズは署名されたデータ型であると感じました
- 特定のアルゴリズムを実装するには、符号なしの算術演算が必要になる場合がありますが、そのようなアルゴリズムを実装するプログラマの種類は、「ラウンド処理」する知識も持っています。符号付きデータ型を使用した符号なし算術の実行
ほとんどの場合、それは合理的な決定でした。おそらく、私は持っているでしょう:
- バイトを符号なしにするか、少なくともこの名前の異なるデータ型に対して、符号付き/符号なしの代替を提供しました(署名することは整合性に優れていますが、いつ符号付きバイトが必要になりますか?)
- 'short'を廃止(最後に16ビット符号付き算術を使用したのはいつですか?)
それでも、少し気を遣うと、32ビットまでの符号なしの値に対する操作は悪くありません。ほとんどの人は、符号なしの64ビットの除算や比較を必要としません。
これは古い質問であり、patは簡単に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に戻す必要はありません)。ただし、符号なしのプリミティブデータ型は確かに存在します。符号なしバイトも見たかったのですが、メモリコストを2倍にして、代わりにcharを使用することは実行可能なオプションです。
編集
JDK8には、 の新しいAPIがあります。 Long
および
。 long
および int
の値を符号なしの値として扱う際のヘルパーメソッドを提供する整数
-
compareUnsigned
-
divideUnsigned
-
parseUnsignedInt
-
parseUnsignedLong
-
remainderUnsigned
-
toUnsignedLong
-
toUnsignedString
さらに、 Guava は、整数型で同様のことを行うための多くのヘルパーメソッドを提供しますこれは、 unsigned
整数のネイティブサポートの欠如によって残されたギャップを埋めるのに役立ちます。
Javaには符号なしの型、または少なくとも1つがあります。charは符号なしのshortです。そのため、Goslingが言い訳をしたとしても、他に符号なしの型が存在しないのは彼の無知にすぎません。
ショートタイプ:ショートパンツは常にマルチメディアに使用されます。理由は、2つのサンプルを単一の32ビット符号なしlongに収め、多くの演算をベクトル化できるからです。 8ビットデータと符号なしバイトでも同じです。ベクトル化のためにレジスタに4つまたは8つのサンプルを収めることができます。
署名されたintと署名されていないintが式で混合されるとすぐに物事が乱雑になり始め、おそらく情報を失うでしょう 。 Javaを署名されたintに制限すると、事態は本当に明確になります。署名された/署名されていないビジネス全体を心配する必要はありませんが、バイトの8番目のビットを見逃すことがあります。
http://skeletoncoder.blogspot.com/ 2006/09 / java-tutorials-why-no-unsigned.html
この男は、C標準が符号なしおよび符号付き整数を含む演算を符号なしとして扱うと定義しているためだと言います。これにより、負の符号付き整数が大きな符号なし整数にロールバックされ、バグが発生する可能性があります。
Javaはそのままでも大丈夫だと思います。符号なしのJavaを追加すると、Javaが複雑になり、あまり利益が得られません。 単純化された整数モデルであっても、ほとんどのJavaプログラマーは基本的な数値型の動作を知りません。本 Java Puzzlers を使用して、考えられる誤解を確認してください。
実用的なアドバイスとして:
-
値がいくぶん任意のサイズであり、
int
に収まらない場合は、long
を使用します。long
に収まらない場合は、BigInteger
を使用します。 -
スペースを節約する必要がある場合は、配列にのみ小さい型を使用してください。
-
正確に64/32/16/8ビットが必要な場合は、
long
/int
/short
/を使用しますバイト
を使用して、除算、比較、右シフト、キャストを除き、符号ビットの心配を止めます。
こちらもご覧ください" CからJavaへの乱数ジェネレーターの移植"。
JDK8 では、いくつかのサポートがあります。
Goslingの懸念にもかかわらず、Javaで未署名の型が完全にサポートされることはまだあります。
この投稿は古すぎます。ただし、Java 8以降では、 int
データ型を使用して、最小値が0で最大値が2 の符号なし32ビット整数を表すことができます。 32 − 1。 Integer
クラスを使用して、 int
データ型を符号なし整数として使用し、静的メソッドを compareUnsigned()
、 divideUnsigned()
などが Integer
クラスに追加され、符号なし整数の算術演算をサポートします。
元のJavaリリースの近くに含まれるという話を聞いたことがあります。オークはJavaの前身であり、いくつかの仕様書では、価値のある価値について言及されていました。残念ながら、これらは決してJava言語にはなりませんでした。おそらく時間の制約が原因で、実装されなかったと考えられる人がいる限り。
私はかつてC ++標準委員会の誰かとC ++コースを受講しましたが、これはJavaが符号なし整数を避ける正しい判断をしたことを暗示しています。なぜなら、(1) (2)符号なし整数を使用すると、簡単に作成できますが、整数演算オーバーフローや符号付き型と符号なし型の間の変換時に重要なビットが失われるなど、デバッグが困難な問題が発生します。符号付き整数を使用して誤って0から1を引くと、2 ^ 32-1にラップアラウンドする場合よりもプログラムがクラッシュしやすくなり、バグを見つけやすくなります。コンパイラーと静的分析ツールとランタイムチェックは、符号なし算術を使用することを選択したので、あなたが何をしているかを知っていると仮定します。また、-1のような負の数は、フィールドが無視/デフォルト/設定解除されるなど、有用なものを表すことがよくありますが、符号なしを使用している場合は、2 ^ 32-1などの特別な値を予約する必要があります。
以前は、メモリが制限され、プロセッサが一度に64ビットで自動的に動作しなかったため、すべてのビットがより多くカウントされていたため、符号付きバイトまたは符号なしバイトまたはショートが実際にはるかに重要であり、明らかに正しい設計決定でした。現在、ほとんどすべての通常のプログラミングの場合、符号付き整数を使用するだけで十分であり、プログラムで実際に2 ^ 31-1より大きい値を使用する必要がある場合は、とにかく長い値が必要になります。ロングを使用する領域に入ると、2 ^ 63-1の正の整数では本当にうまくいかない理由を見つけるのがさらに難しくなります。 128ビットプロセッサを使用すると、問題はさらに少なくなります。
あなたの質問は、「Javaが符号なしintをサポートしない理由」です
そして、あなたの質問に対する私の答えは、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 ++であり続けることはありません。私にとっては、そのようなコードでは、途中でスタイルを変更するのは良くありません。
署名付き操作にも静的メソッドを記述して使用することをお勧めします。そのため、コード内に署名付き操作のみが必要な場合を除き、符号付き操作と署名なし操作の両方に演算子と静的メソッドを混在させることはありません。演算子のみを使用しても構いません。
また、 short 、 int 、および long プリミティブタイプの使用を避け、 word 、<代わりにそれぞれstrong> dword および qword を使用します。演算子を使用する代わりに、署名のない操作や署名付きの操作に対して静的メソッドを呼び出します。
符号付き操作のみを実行し、コード内でのみ演算子を使用する場合は、これらのプリミティブタイプ short 、 int 、および long 。
実際には単語、 dword 、および qword は言語に存在しませんありませんが、作成できますそれぞれの新しいクラスとそれぞれの実装は非常に簡単なはずです:
クラス word はプリミティブ型 short のみを保持し、クラス dword はプリミティブ型 int のみを保持しますクラス qword はプリミティブ型 long のみを保持します。すべてのunsignedメソッドとsignedメソッドを静的または選択としてではなく、各クラスに実装できます。つまり、 word クラスに意味名を付けることで、すべての16ビット演算をunsignedとsignedの両方で実装できます。 dword クラスで意味名を指定することによる符号なしと署名の32ビット操作、および qword クラスで意味名を指定することによるすべての64ビット操作。 / p>
各メソッドに多くの異なる名前を付けたくない場合は、Javaでオーバーロードをいつでも使用できます。Javaがそれを削除したことを読むとよいでしょう削除しない!
8 biの演算子ではなくメソッドが必要な場合
unsigned
タイプは純粋な悪だからです。
Cでは、 unsigned-int
が unsigned
を生成するという事実は、さらに悪です。
これは、2回以上私を燃やした問題のスナップショットです:
// 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
は< code> unsigned 。この式は、中央の1つ目の n
光線の signed 位置になることを意図しています。左側の中央の1つ目の光線の位置は-1です。右側の1番目の位置は+1などになります。abs値を取得して delta
の角度を掛けると、 n
番目の光線と中央の角度が得られます1つ。
残念ながら、上記の式には符号なしの悪が含まれており、たとえば-1と評価する代わりに、2 ^ 32-1と評価されました。その後の double
への変換により、バグが封印されました。
unsigned
算術演算の誤用による1つまたは2つのバグの後、余分なビットを取得することは余分なトラブルに見合うかどうか疑問に思う必要があります。可能な限り、算術での unsigned
型の使用を回避しようとしていますが、バイナリマスクなどの非算術演算には引き続き使用しています。
「C」仕様には、実用的な理由でJavaがドロップしたものの、開発者の需要(クロージャなど)とともにゆっくりと戻ってきているジェムがいくつかあります。
この議論に関連しているため、最初のものについて言及します。ポインタ値の符号なし整数演算への準拠。また、このスレッドトピックに関連して、Javaの署名付きの世界で署名なしのセマンティクスを維持することの難しさ。
デニス・リッチーの代替エゴを入手して、ゴズリングのデザインチームに「無限大でゼロ」を与えることを提案したので、すべてのアドレスオフセット要求が最初に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」要素の操作に一致する「set」操作の候補を以下に示します。
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
}
不幸な副作用が1つ考えられます。 Java組み込みデータベースでは、32ビットのidフィールドで使用できるIDの数は、2 ^ 32ではなく2 ^ 31です(約40億ではなく、約20億)。
私見の理由は、彼らがその怠慢を実装/修正するには遅すぎるからです。 C / C ++プログラマーが符号なし、構造体、共用体、ビットフラグを理解していないことを示唆しています...ただの馬鹿げています。
Ether C言語のプログラミングを始めようとしているベーシック/ bash / javaプログラマーと話していましたが、この言語を実際に知らずに、または自分の心の外で話しているだけです。 ;)
ファイルまたはハードウェアのフォーマットで毎日対処するとき、あなたが疑問に思っているのは、一体何を考えていたのか
ここでの良い例は、符号なしバイトを自己回転ループとして使用することです。 最後の文を理解していない人のために、一体どうやって自分をプログラマーと呼んでいますか。
DC