Prise en charge de l’arithmétique non signée Java 8

Support arithmétique Java 8 non signé

1. Vue d'ensemble

Dès l'aube de Java, tous les types de données numériques sont signés. Dans de nombreuses situations, cependant, il est nécessaire d'utiliser des valeurs non signées. Par exemple, si nous comptons le nombre d'occurrences d'un événement, nous ne voulons pas rencontrer de valeur négative.

La prise en charge de l’arithmétique non signée fait enfin partie du kit JDK à partir de la version 8. This support came in the form of the Unsigned Integer API, primarily containing static methods in the Integer and Long classes.

Dans ce tutoriel, nous allons passer en revue cette API et donner des instructions sur la façon d'utiliser correctement les nombres non signés.

2. Représentations au niveau du bit

Pour comprendre comment gérer les nombres signés et non signés, examinons d'abord leur représentation au niveau du bit.

In Java, numbers are encoded using the two’s complement system. Ce codage implémente de nombreuses opérations arithmétiques de base, y compris l'addition, la soustraction et la multiplication, de la même manière, que les opérandes soient signés ou non.

Les choses devraient être plus claires avec un exemple de code. Par souci de simplicité, nous utiliserons des variables du type de données primitifbyte. Les opérations sont similaires pour les autres types numériques intégraux, tels queshort,int oulong.

Supposons que nous ayons un typebyte avec la valeur de100. Ce nombre a la représentation binaire0110_0100.

Doublons cette valeur:

byte b1 = 100;
byte b2 = (byte) (b1 << 1);

L'opérateur de décalage gauche dans le code donné déplace tous les bits de la variableb1 d'une position vers la gauche, ce qui rend techniquement sa valeur deux fois plus grande. La représentation binaire de la variableb2 sera alors1100_1000.

Dans un système de type non signé, cette valeur représente un nombre décimal équivalent à2^7 + 2^6 + 2^3 ou200. Néanmoins,in a signed system, the left-most bit works as the sign bit. Par conséquent, le résultat est -2^7 + 2^6 + 2^3 ou-56.

Un test rapide peut vérifier le résultat:

assertEquals(-56, b2);

Nous pouvons voir que les calculs des nombres signés et non signés sont les mêmes. Differences only appear when the JVM interprets a binary representation as a decimal number.

Les opérations d'addition, de soustraction et de multiplication peuvent fonctionner avec des nombres non signés sans aucune modification du JDK. D'autres opérations, telles que la comparaison ou la division, gèrent différemment les nombres signés et non signés.

C'est ici qu'intervient l'API Unsigned Integer.

3. L'API des nombres entiers non signés

L'API Unsigned Integer prend en charge l'arithmétique des entiers non signés dans Java 8. La plupart des membres de cette API sont des méthodes statiques dans les classesInteger etLong.

Les méthodes de ces classes fonctionnent de la même manière. Nous allons donc nous concentrer uniquement sur la classeInteger, en laissant de côté la classeLong par souci de concision.

3.1. Comparaison

La classeInteger définit une méthode nomméecompareUnsigned pour comparer des nombres non signés. This method considers all binary values unsigned, ignoring the notion of the sign bit.

Commençons par deux nombres aux limites du type de donnéesint:

int positive = Integer.MAX_VALUE;
int negative = Integer.MIN_VALUE;

Si nous comparons ces nombres comme des valeurs signées,positive est évidemment supérieur ànegative:

int signedComparison = Integer.compare(positive, negative);
assertEquals(1, signedComparison);

Lors de la comparaison de nombres en tant que valeurs non signées, le bit le plus à gauche est considéré comme le bit le plus significatif au lieu du bit de signe. Ainsi, le résultat est différent,positive étant inférieur ànegative:

int unsignedComparison = Integer.compareUnsigned(positive, negative);
assertEquals(-1, unsignedComparison);

Cela devrait être plus clair si on regarde la représentation binaire de ces nombres:

  • MAX_VALUE0111_11111111

  • MIN_VALUE1000_00000000

Lorsque le bit le plus à gauche est un bit de valeur régulière,MIN_VALUE est une unité plus grand queMAX_VALUE dans le système binaire. Ce test confirme que:

assertEquals(negative, positive + 1);

3.2. Division et Modulo

Tout comme l'opération de comparaison,the unsigned division and modulo operations process all bits as value bits. Les quotients et les restes sont donc différents lorsque nous effectuons ces opérations sur des nombres signés et non signés:

int positive = Integer.MAX_VALUE;
int negative = Integer.MIN_VALUE;

assertEquals(-1, negative / positive);
assertEquals(1, Integer.divideUnsigned(negative, positive));

assertEquals(-1, negative % positive);
assertEquals(1, Integer.remainderUnsigned(negative, positive));

3.3. Analyse

Lors de l'analyse d'unString à l'aide de la méthodeparseUnsignedInt,the text argument can represent a number greater than MAX_VALUE.

Une grande valeur comme celle-ci ne peut pas être analysée avec la méthodeparseInt, qui ne peut gérer que la représentation textuelle des nombres deMIN_VALUE àMAX_VALUE.

Le cas de test suivant vérifie les résultats de l'analyse:

Throwable thrown = catchThrowable(() -> Integer.parseInt("2147483648"));
assertThat(thrown).isInstanceOf(NumberFormatException.class);

assertEquals(Integer.MAX_VALUE + 1, Integer.parseUnsignedInt("2147483648"));

Notez que la méthodeparseUnsignedInt peut analyser une chaîne indiquant un nombre supérieur àMAX_VALUE, mais ne parviendra pas à analyser toute représentation négative.

3.4. Mise en page

Semblable à l'analyse, lors du formatage d'un nombre, une opération non signée considère tous les bits comme des bits de valeur. Par conséquent,we can produce the textual representation of a number about twice as large as MAX_VALUE.

Le scénario de test suivant confirme le résultat du formatage deMIN_VALUE dans les deux cas - signé et non signé:

String signedString = Integer.toString(Integer.MIN_VALUE);
assertEquals("-2147483648", signedString);

String unsignedString = Integer.toUnsignedString(Integer.MIN_VALUE);
assertEquals("2147483648", unsignedString);

4. Avantages et inconvénients

De nombreux développeurs, en particulier ceux issus d'un langage qui prend en charge les types de données non signés, tels que C, se félicitent de l'introduction d'opérations arithmétiques non signées. Cependant,this isn’t necessarily a good thing.

Il y a deux raisons principales à la demande de numéros non signés.

Premièrement, il existe des cas pour lesquels une valeur négative ne peut jamais se produire, et l'utilisation d'un type non signé peut empêcher une telle valeur en premier lieu. Deuxièmement, avec un type non signé, nous pouvonsdouble the range of usable positive values par rapport à son homologue signé.

Analysons la justification de l’attrait des nombres non signés.

Lorsqu'une variable doit toujours être non négative, une valeur inférieure à0 peut être utile pour indiquer une situation exceptionnelle.

Par exemple, la méthodeString.indexOf renvoie la position de la première occurrence d'un certain caractère dans une chaîne. L'indice -1 peut facilement dénoter l'absence d'un tel caractère.

L'autre raison des nombres non signés est l'expansion de l'espace de valeur. Cependant,if the range of a signed type isn’t enough, it’s unlikely that a doubled range would suffice.

Dans le cas où un type de données n'est pas assez grand, nous devons utiliser un autre type de données qui prend en charge des valeurs beaucoup plus grandes, comme l'utilisation delong au lieu deint, ouBigInteger plutôt quelong.

Un autre problème avec l'API des nombres entiers non signés est que la forme binaire d'un nombre est la même, qu'il soit signé ou non. C'est donceasy to mix signed and unsigned values, which may lead to unexpected results.

5. Conclusion

Le support de l'arithmétique non signée en Java est venu à la demande de nombreuses personnes. Cependant, les avantages qu'il apporte ne sont pas clairs. Nous devons faire preuve de prudence lorsque nous utilisons cette nouvelle fonctionnalité pour éviter des résultats inattendus.

Comme toujours, le code source de cet article est disponibleover on GitHub.