Question

Pourquoi Java n'inclut-il pas la prise en charge des entiers non signés?

Cela me semble être une omission bizarre, étant donné qu’elles permettent d’écrire du code moins susceptible de produire des débordements sur une entrée d’une taille inattendue.

De plus, utiliser des entiers non signés peut être une forme d'auto-documentation, car ils indiquent que la valeur que l'entité non signée était censée contenir n'est jamais supposée être négative.

Enfin, dans certains cas, les entiers non signés peuvent être plus efficaces pour certaines opérations, telles que la division.

Quel est l'inconvénient d'inclure ces éléments?

Était-ce utile?

La solution

Ceci est tiré d'un entrevue avec Gosling et d'autres sur la simplicité:

  

Gosling: Pour moi en tant que concepteur de langage, ce que je ne me considère pas vraiment comme de nos jours, ce qui est "simple" le sens réel était de savoir si je pouvais espérer que J. Random Developer tienne la spécification en tête. Cette définition dit, par exemple, que Java ne l’est pas - et en fait, beaucoup de ces langages se retrouvent avec beaucoup de casse-tête, des choses que personne ne comprend vraiment. Interrogez n'importe quel développeur C sur les non-signés et vous découvrirez bientôt que presque aucun développeur C ne comprend réellement ce qui se passe avec l'arithmétique non-signée, non signée. Des choses comme ça ont rendu C complexe. La partie langage de Java est, je pense, assez simple. Les bibliothèques que vous devez rechercher.

Autres conseils

En lisant entre les lignes, je pense que la logique ressemblait à ceci:

  • En règle générale, les concepteurs Java souhaitaient simplifier le répertoire des types de données disponibles
  • à des fins quotidiennes, ils estimaient que le besoin le plus courant était de types de données signés
  • pour implémenter certains algorithmes, une arithmétique non signée est parfois nécessaire, mais le type de programmeurs qui implémenterait de tels algorithmes aurait également les connaissances nécessaires pour "contourner" faire de l'arithmétique non signée avec des types de données signés
La plupart du temps, je dirais que c'était une décision raisonnable. Peut-être que j'aurais:

  • made byte unsigned, ou du moins ont fourni une alternative signée / unsigned, éventuellement avec des noms différents, pour ce type de données (le fait de le signer est bon pour la cohérence, mais quand avez-vous besoin d'un octet signé?)
  • a supprimé "court" (quand avez-vous utilisé l'arithmétique signée 16 bits pour la dernière fois?)

Néanmoins, avec un peu de kludging, les opérations sur des valeurs non signées jusqu'à 32 bits ne sont pas trop mauvaises, et la plupart des gens n'ont pas besoin de division ou de comparaison 64 bits non signée.

Il s’agit d’une question plus ancienne et pat a brièvement mentionné le caractère, j’ai juste pensé que je devrais développer cette question pour les autres qui examineront la question plus tard. Examinons de plus près les types primitifs Java:

octet - entier signé de 8 bits

short - entier signé de 16 bits

int - entier signé 32 bits

long - entier signé de 64 bits

char - caractère 16 bits (entier non signé)

Bien que char ne supporte pas l'arithmétique unsigned , il peut essentiellement être traité comme un entier unsigned . Vous devrez convertir explicitement les opérations arithmétiques en char , mais cela vous fournit un moyen de spécifier des nombres 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--;

Oui, il n’existe pas de support direct pour les entiers non signés (bien entendu, je n’aurais pas à relancer la plupart de mes opérations en caractère s'il y avait un support direct). Cependant, il existe certainement un type de données primitif non signé. J'aurais bien aimé aussi voir un octet non signé, mais je suppose que doubler le coût de la mémoire et utiliser plutôt un caractère est une option viable.

Modifier

Avec JDK8, il existe de nouvelles API pour Long et Entier qui fournit des méthodes d'assistance lors du traitement des valeurs long et int en tant que valeurs non signées.

  • compareUnsigned
  • divideUnsigned
  • parseUnsignedInt
  • parseUnsignedLong
  • resteUnsigned
  • toUnsignedLong
  • toUnsignedString

De plus, Guava fournit un certain nombre de méthodes d'assistance permettant d'effectuer des opérations similaires pour les types entiers. ce qui permet de combler le vide laissé par le manque de prise en charge native des entiers unsigned .

Java a des types non signés, ou au moins un: char est un short non signé. Donc, quelle que soit l'excuse que Gosling avance, c'est simplement son ignorance qui fait qu'il n'y a pas d'autres types non signés.

Aussi les types courts: les courts métrages sont utilisés tout le temps pour le multimédia. La raison en est que vous pouvez adapter 2 échantillons dans un seul long non signé 32 bits et vectoriser de nombreuses opérations. Même chose avec les données 8 bits et l'octet non signé. Vous pouvez ajuster 4 ou 8 échantillons dans un registre pour la vectorisation.

Dès que les expressions signée et non signée sont mélangées dans une expression, les choses commencent à devenir compliquées et vous perdrez probablement des informations. Restreindre Java aux fichiers signés ne fait que clarifier les choses. Je suis heureux de ne pas avoir à m'inquiéter de toute l'affaire signée / non signée, même si parfois je manque le 8ème bit d'un octet.

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

Ce type dit que parce que la norme C définit les opérations impliquant des inns non signés et signés à traiter comme non signés. Cela pourrait faire rouler des entiers signés négatifs dans un grand entier non signé, ce qui pourrait causer des bogues.

Je pense que Java est correct tel quel, ajouter un signe non signé le compliquerait sans grand gain. Même avec le modèle entier simplifié, la plupart des programmeurs Java ne savent pas comment se comportent les types numériques de base - il suffit de lire le livre Java Puzzlers pour connaître les idées fausses que vous pourriez avoir.

Pour des conseils pratiques:

  • Si vos valeurs ont une taille quelque peu arbitraire et ne rentrent pas dans int , utilisez long . S'ils ne rentrent pas dans long , utilisez BigInteger .

  • Utilisez les types plus petits uniquement pour les tableaux lorsque vous devez économiser de l'espace.

  • Si vous avez besoin de 64/32/16/8 bits exactement, utilisez long / int / court / byte et ne vous souciez plus du bit de signe, sauf pour la division, la comparaison, le décalage à droite et le transtypage.

Voir aussi cette réponse à propos de " porter un générateur de nombres aléatoires de C à Java ".

Le JDK8 est pris en charge.

Nous constatons peut-être encore une prise en charge complète des types non signés en Java, malgré les préoccupations de Gosling.

Je sais que ce post est trop vieux; toutefois, pour votre intérêt, dans Java 8 et versions ultérieures, vous pouvez utiliser le type de données int pour représenter un entier 32 bits non signé, qui a une valeur minimale de 0 et une valeur maximale de 2 32 -1. Utilisez la classe Integer pour utiliser le type de données int en tant qu'entier non signé et des méthodes statiques telles que compareUnsigned () , divideUnsigned () etc. ont été ajoutés à la classe Integer pour prendre en charge les opérations arithmétiques des entiers non signés.

J'ai entendu dire qu'ils devaient être inclus à proximité de la version Java d'origine. Oak était le précurseur de Java et, dans certains documents de spécification, il était fait mention de valeurs usignées. Malheureusement, ceux-ci n'ont jamais été intégrés au langage Java. Pour autant que quiconque ait pu déterminer qu’ils n’avaient tout simplement pas été mis en œuvre, probablement en raison de contraintes de temps.

J'ai déjà suivi un cours de C ++ avec un membre du comité de normalisation C ++ qui a laissé entendre que Java avait pris la bonne décision pour éviter d'avoir des entiers non signés, car (1) la plupart des programmes utilisant des entiers non signés peuvent tout aussi bien fonctionner avec des entiers signés. Plus naturel en termes de pensée, et (2) utiliser des entiers non signés résulte en beaucoup de choses faciles à créer mais difficiles à résoudre, comme le débordement arithmétique d’entiers et la perte de bits significatifs lors de la conversion entre types signés et non signés. Si, par erreur, vous soustrayez 1 de 0 en utilisant des entiers signés, cela provoque souvent une panne plus rapide de votre programme et facilite la recherche du bogue que s'il contournait jusqu'à 2 ^ 32 - 1, et les compilateurs, les outils d'analyse statique et les vérifications d'exécution devaient supposez que vous savez ce que vous faites depuis que vous avez choisi d'utiliser l'arithmétique non signée. De plus, les nombres négatifs tels que -1 peuvent souvent représenter quelque chose d’utile, comme un champ ignoré / par défaut / non défini tandis que si vous utilisiez non signé, vous devez réserver une valeur spéciale telle que 2 ^ 32 - 1 ou quelque chose de similaire.

Il y a bien longtemps, lorsque la mémoire était limitée et que les processeurs ne fonctionnaient pas automatiquement sur 64 bits à la fois, chaque bit comptait beaucoup plus. Avoir signé vs octets ou shorts non signés importait en réalité beaucoup plus souvent et constituait évidemment la bonne décision de conception. . Aujourd’hui, utiliser un entier signé est plus que suffisant dans la plupart des cas de programmation classiques, et si votre programme doit vraiment utiliser des valeurs supérieures à 2 ^ 31 - 1, vous voulez souvent un long. Une fois que vous êtes sur le terrain de l'utilisation de longs, il est encore plus difficile de trouver une raison pour laquelle vous ne pouvez vraiment pas vous en tirer avec 2 entiers positifs 63 ~ 1. Chaque fois que nous allons aux processeurs 128 bits, ce sera encore moins un problème.

Votre question est "Pourquoi Java ne prend-il pas en charge les entrées non signées"?

Et ma réponse à votre question est que Java souhaite que tous ses types primitifs: octet , caractère , court , int et long doivent être traités comme octet , mot , dword et qword respectivement, exactement comme dans un assemblage, et les opérateurs Java sont des signatures (opérations ) signalisées sur tous ses types primitifs, à l'exception de char , mais uniquement sur char ils ne sont signés que 16 bits.

Les méthodes statiques supposent donc que les opérations unsigned soient également en 32 bits et en 64 bits.

Vous avez besoin de la classe finale, dont les méthodes statiques peuvent être appelées pour les opérations unsigned .

Vous pouvez créer cette classe finale, l'appeler comme vous le souhaitez et mettre en œuvre ses méthodes statiques.

Si vous ne savez pas comment implémenter les méthodes statiques, cliquez sur ce lien . peut vous aider.

À mon avis, Java n'est pas similaire au C ++ du tout , s'il ni ne prend pas en charge les types non signés ni surcharge d'opérateur, donc je pense que Java devrait être traité comme un langage complètement différent du C ++ et du C.

Il est également complètement différent dans le nom des langues.

Donc, je ne recommande pas en Java de taper du code similaire à C et je ne recommande pas de taper du code similaire à C ++, car en Java, vous ne pourrez pas faire ce que vous voulez faire ensuite. en C ++, c’est-à-dire que le code ne sera plus du tout un code C ++ et pour moi, c’est mauvais de coder comme ça, de changer le style au milieu.

Je vous recommande d'écrire et d'utiliser des méthodes statiques également pour les opérations signées. Vous ne verrez donc pas dans le code une combinaison d'opérateurs et de méthodes statiques pour les opérations signées et non signées, à moins que vous n'ayez besoin que d'opérations signées dans le code, et vous pouvez utiliser uniquement les opérateurs.

Je vous recommande également d'éviter d'utiliser les types primitifs short , int et long , et d'utiliser word , < strong> dword et qword , et appelez les méthodes statiques pour les opérations non signées et / ou signées au lieu d'utiliser des opérateurs.

Si vous êtes sur le point d'effectuer uniquement des opérations signées et d'utiliser les opérateurs uniquement dans le code, vous pouvez utiliser ces types primitifs short , int et long .

En réalité, mot , dword et qword n'existe pas dans le langage, mais vous pouvez créer nouvelle classe pour chacun et la mise en œuvre de chacun devrait être très facile:

La classe mot contient uniquement le type primitif short , la classe dword contient le type primitif int . et la classe qword ne contient que le type primitif long . Désormais, toutes les méthodes non signées et signées, statiques ou non, peuvent être implémentées dans chaque classe, c'est-à-dire toutes les opérations 16 bits non signées et signées en donnant des noms de signification à la classe word , le tout les opérations 32 bits non signées et signées en donnant des noms de sens sur la classe dword et toutes les opérations 64 bits non signées et signées en donnant des noms de sens sur la classe qword .

Si vous n'aimez pas donner trop de noms différents pour chaque méthode, vous pouvez toujours utiliser la surcharge en Java, il est bon de savoir que Java ne l'a pas supprimé aussi!

Si vous souhaitez des méthodes plutôt que des opérateurs pour les opérations signées 8 bits et des méthodes pour les opérations non signées 8 bits sans opérateur, vous pouvez créer la classe Octet (notez que la première lettre ' B 'est un capital, donc ce n'est pas le type primitif octet ) et implémentez les méthodes de cette classe.

À propos du passage par valeur et du renvoi par référence:

Si je ne me trompe pas, comme dans C #, les objets primitifs sont passés naturellement par la valeur, mais les objets de classe sont passés naturellement par la référence, ce qui signifie que les objets de type Octet , , dword et qword seront transmis par référence et non par valeur. Je souhaite que Java ait des objets struct similaires à ceux de C #, afin que tous les octets , les mots , les dword et les qword puissent être mis en œuvre de manière struct au lieu de classe , ils sont donc passés par défaut par valeur et non par référence, comme tout objet struct en C #, comme les types primitifs, sont passés par valeur et non par référence, mais parce que Java est pire que C # et que nous devons en tenir compte, il n’ya que les classes et les interfaces, qui sont passées par référence et non par valeur. Donc, si vous voulez passer les octets , les mots , dword et les qword objets par valeur et non par référence, comme Pour tout autre objet de classe en Java et aussi en C #, vous devrez simplement utiliser le constructeur de copie et le tour est joué.

C’est la seule solution à laquelle je puisse penser. J'aimerais seulement pouvoir typer les types primitifs en mots, dword et qword, mais Java ne prend en charge ni typedef ni l'utilisation du tout, contrairement à C # qui prend en charge using , ce qui est équivalent à la typedef du C.

À propos de la sortie:

Pour la même séquence de bits , vous pouvez les imprimer de différentes manières: en binaire, en décimal (comme la signification de% u dans C printf), en octal (comme la signification de% o dans C printf), en tant qu’hexadécimal (comme la signification de% x dans C printf) et comme un entier (comme la signification du% d dans C printf).

Notez que C printf ne connaît pas le type des variables transmises en tant que paramètres à la fonction. Par conséquent, printf connaît le type de chaque variable uniquement à partir de l'objet char * transmis au premier paramètre de la fonction.

Ainsi, dans chacune des classes: Octet , mot , dword et qword , vous pouvez implémenter l'impression méthode et obtenir la fonctionnalité de printf, même si le type primitif de la classe est signé, vous pouvez toujours l’imprimer comme non signé en suivant un algorithme impliquant des opérations logiques et des opérations de décalage pour obtenir les chiffres à imprimer vers la sortie.

Malheureusement, le lien que je vous ai donné ne montre pas comment implémenter ces méthodes d'impression, mais je suis sûr que vous pouvez rechercher Google pour les algorithmes dont vous avez besoin pour implémenter ces méthodes d'impression.

C'est tout ce que je peux répondre à votre question et vous suggérer.

Parce que le type unsigned est un pur mal.

Le fait qu'en C unsigned - int produise unsigned est encore plus pervers.

Voici un instantané du problème qui m'a brûlé plus d'une fois:

// 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 ...
}

Avez-vous déjà remarqué le bogue? J'avoue que je ne l'ai vu qu'après être intervenu avec le débogueur.

Parce que n est du type non signé size_t , l'expression entière n - (rays.size () - 1) / 2 est évaluée par < code> non signé . Cette expression est censée être une position signée du rayon n du centre: le premier rayon du centre du côté gauche aurait la position -1, le premier à droite aurait la position +1, etc. Après avoir pris la valeur abs et multiplié par l'angle delta , j'obtiendrais l'angle entre n et le milieu un.

Malheureusement pour moi, l'expression ci-dessus contenait le mal non signé et au lieu d'évaluer -1, par exemple, elle s'évalue à 2 ^ 32-1. La conversion ultérieure en double a scellé le bogue.

Après un bogue ou deux causé par une mauvaise utilisation de l'arithmétique unsigned , il faut commencer à se demander si le bit supplémentaire obtenu vaut la peine d'être compensé. J'essaie, autant que possible, d'éviter toute utilisation de types unsigned en arithmétique, bien que je l'utilise quand même pour des opérations non arithmétiques telles que les masques binaires.

Il y a quelques pierres précieuses dans la spécification 'C' que Java a abandonnées pour des raisons pragmatiques, mais qui reviennent lentement à la demande des développeurs (fermetures, etc.).

Je mentionne un premier parce qu'il est lié à cette discussion; l'adhérence des valeurs de pointeur à l'arithmétique entière non signée. Et, par rapport à ce sujet de fil, la difficulté de maintenir la sémantique Unsigned dans le monde signé de Java.

Je suppose que si l'on demandait à un alter ego de Dennis Ritchie d'avertir l'équipe de concepteurs de Gosling, il aurait suggéré de donner à Signed un "zéro à l'infini", de sorte que toutes les demandes de décalage d'adresse ajoutent d'abord leur TAILLE D'ANNEAU ALGÉBRE pour éviter valeurs négatives.

De cette façon, aucun décalage jeté dans le tableau ne peut jamais générer un SEGFAULT. Par exemple, dans une classe encapsulée que j'appelle RingArray, il faut un comportement non signé entre doublets - dans "boucle auto-tournante". contexte:

// ...
// 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 ci-dessus ne «obtiendrait» jamais un index négatif, même si un demandeur malveillant tentait de le faire. N'oubliez pas qu'il existe également de nombreuses demandes légitimes de demandes de valeurs d'index antérieures (négatives).

Remarque: le module% externe dé-référence les requêtes légitimes, tandis que le module interne masque les malices flagrants des négatifs plus négatifs que -modulus. Si cela devait apparaître dans un fichier Java + .. + 9 || 8 + .. + spec, le problème deviendrait alors véritablement un "programmeur qui ne peut pas" faire pivoter " DEFAUT '.

Je suis sûr que le prétendu "déficit" en Java non signé peut être compensé avec le one-liner ci-dessus.

PS: Juste pour donner un contexte à la gestion RingArray ci-dessus, voici une opération 'set' candidate pour correspondre à l'opération d'élément 'get' ci-dessus:

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
}

Je peux penser à un effet secondaire malheureux. Dans les bases de données Java intégrées, le nombre d'identifiants que vous pouvez avoir avec un champ d'identifiant 32 bits est de 2 ^ 31, et non de 2 ^ 32 (environ 2 milliards, pas environ 4 milliards).

La raison en est qu’ils sont / étaient trop paresseux pour mettre en œuvre / corriger cette erreur. Suggérer que les programmeurs C / C ++ ne comprennent pas non signés, structure, union, indicateur de bit ... est simplement absurde.

Ether vous parliez avec un programmeur basique / bash / java sur le point de commencer à programmer en C, sans aucune connaissance réelle de ce langage ou vous ne parlez que par votre propre esprit. ;)

lorsque vous traitez tous les jours des formats de fichiers ou de matériel, vous vous demandez ce qu’ils pensaient vraiment.

Un bon exemple ici consisterait à utiliser un octet non signé en tant que boucle à rotation automatique. Pour ceux d'entre vous qui ne comprennent pas la dernière phrase, comment vous appelez-vous programmeur?

DC

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top