¿Por qué Java no admite ints sin signo?

¿Por qué Java no incluye soporte para enteros sin signo?

Me parece que es una omisión extraña, dado que permiten escribir código que es menos probable que produzca desbordamientos en entradas inesperadamente grandes.

Además, el uso de enteros sin signo puede ser una forma de auto-documentación, ya que indican que el valor que el unsigned int pretendía contener nunca se supone que sea negativo.

Por último, en algunos casos, los enteros sin signo pueden ser más eficientes para ciertas operaciones, como la división.

¿Cuál es la desventaja de incluir estos?

Esto es de una entrevista con Gosling y otros , sobre la simplicidad:

Gosling: Para mí, como diseñador de lenguaje, en lo que realmente no me cuento en estos días, lo que “simple” realmente terminó en significado fue si podía esperar que J. Random Developer mantuviera las especificaciones en su cabeza. Esa definición dice que, por ejemplo, Java no lo es, y de hecho, muchos de estos idiomas terminan con muchos casos de esquina, cosas que nadie entiende realmente. Cuestione cualquier desarrollador de C sobre unsigned, y muy pronto descubrirá que casi ningún desarrollador C en realidad entiende lo que sucede con unsigned, lo que es la aritmética unsigned. Cosas como esa hicieron que C sea complejo. La parte de lenguaje de Java es, creo, bastante simple. Las bibliotecas que debes buscar

Leyendo entre líneas, creo que la lógica era algo como esto:

  • en general, los diseñadores de Java querían simplificar el repertorio de tipos de datos disponibles
  • para los propósitos cotidianos, sentían que la necesidad más común era para los tipos de datos firmados
  • para la implementación de ciertos algoritmos, la aritmética sin signo a veces es necesaria, pero el tipo de progtwigdores que implementarían tales algoritmos también tendría el conocimiento para “trabajar en redondo” haciendo aritmética sin firmar con tipos de datos firmados

Sobre todo, diría que fue una decisión razonable. Posiblemente, tendría:

  • hecho byte sin firmar, o al menos haber proporcionado alternativas firmadas / no firmadas, posiblemente con diferentes nombres, para este tipo de datos (hacer que sea firmado es bueno para la coherencia, pero ¿cuándo necesitas un byte firmado?)
  • eliminado con ‘corto’ (¿cuándo utilizó por última vez la aritmética de 16 bits firmada?)

Aún así, con un poco de kludging, las operaciones en valores sin firmar de hasta 32 bits no son demasiado malas, y la mayoría de la gente no necesita una división o comparación sin signo de 64 bits.

Esta es una pregunta anterior y Pat mencionó brevemente a char, solo pensé que debería ampliar esto para otros que lo verán en el futuro. Echemos un vistazo más de cerca a los tipos primitivos de Java:

byte – entero con signo de 8 bits

short – entero de 16 bits con signo

int – entero de 32 bits con signo

long – entero con signo de 64 bits

charchar 16 bits (entero sin signo)

Aunque char no es compatible con aritmética unsigned , esencialmente se puede tratar como un entero unsigned signo. Debería volver a realizar operaciones aritméticas explícitamente en char , pero le proporciona una forma de especificar números 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--; 

Sí, no hay soporte directo para enteros sin signo (obviamente, no tendría que volver a convertir la mayoría de mis operaciones en char si hubiera soporte directo). Sin embargo, ciertamente existe un tipo de datos primitivo sin signo. Me hubiera gustado también haber visto un byte sin signo, pero supongo que duplicar el costo de la memoria y utilizar el uso de caracteres es una opción viable.


Editar

Con JDK8 hay nuevas API para Long e Integer que proporcionan métodos de ayuda al tratar valores long e int como valores sin signo.

  • compareUnsigned
  • divideUnsigned
  • parseUnsignedInt
  • parseUnsignedLong
  • remainderUnsigned
  • toUnsignedLong
  • toUnsignedString

Además, Guava proporciona una serie de métodos de ayuda para hacer cosas similares para los tipos enteros, lo que ayuda a cerrar el espacio dejado por la falta de soporte nativo para enteros unsigned .

Java tiene tipos sin firmar, o al menos uno: char es un corto sin firmar. Entonces, cualquiera que sea la excusa que vomita Gosling, es solo su ignorancia por qué no hay otros tipos sin firmar.

También los tipos cortos: los cortos se usan todo el tiempo para multimedia. La razón es que puede caber 2 muestras en una única longitud sin signo de 32 bits y vectorizar muchas operaciones. Lo mismo con datos de 8 bits y bytes sin signo. Puede caber 4 u 8 muestras en un registro para vectorizar.

Tan pronto como las notas firmadas y sin firmar se mezclan en una expresión, las cosas comienzan a complicarse y es probable que pierda información. Restringir Java a los inicios de sesión solo realmente aclara las cosas. Me alegro de no tener que preocuparme por todo el negocio firmado / no firmado, aunque a veces pierdo el octavo bit en un byte.

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

Este tipo dice que el estándar C define operaciones que involucran ints sin firmar y firmados para ser tratados como unsigned. Esto podría causar que los enteros con signo negativo rodaran dentro de una gran int sin firmar, lo que podría causar errores.

Creo que Java está bien, y agregar unsigned lo complicaría sin mucha ganancia. Incluso con el modelo de entero simplificado, la mayoría de los progtwigdores de Java no saben cómo se comportan los tipos numéricos básicos; basta con leer el libro Puzzles de Java para ver qué conceptos erróneos puede contener.

En cuanto a consejos prácticos:

  • Si sus valores son de algún tamaño arbitrario y no encajan en int , use long . Si no encajan en el uso long BigInteger .

  • Utilice los tipos más pequeños solo para matrices cuando necesite ahorrar espacio.

  • Si necesita exactamente 64/32/16/8 bits, use long / int / short / byte y deje de preocuparse por el bit de signo, a excepción de división, comparación, desplazamiento a la derecha y fundición.

Ver también esta respuesta sobre “portar un generador de números aleatorios desde C a Java”.

Con JDK8 tiene algo de apoyo para ellos.

Todavía podemos ver soporte completo de tipos sin firmar en Java a pesar de las preocupaciones de Gosling.

Sé que esta publicación es demasiado antigua; sin embargo, para su interés, en Java 8 y posterior, puede usar el tipo de datos int para representar un entero de 32 bits sin signo, que tiene un valor mínimo de 0 y un valor máximo de 2 32 -1. Utilice la clase Integer para usar el tipo de datos int como un entero sin signo y los métodos estáticos como compareUnsigned() , divideUnsigned() etc. se han agregado a la clase Integer para admitir las operaciones aritméticas para enteros sin signo.

He escuchado historias de que iban a incluirse cerca de la versión original de Java. Oak fue el precursor de Java, y en algunos documentos de especificaciones se mencionaron los valores asignados. Desafortunadamente, estos nunca llegaron al lenguaje Java. Por lo que cualquiera ha podido descubrir, simplemente no se implementaron, probablemente debido a una restricción de tiempo.

Una vez tomé un curso de C ++ con alguien en el comité de estándares de C ++ que implicaba que Java tomó la decisión correcta para evitar tener enteros sin signo porque (1) la mayoría de los progtwigs que usan enteros sin signo pueden funcionar igual con enteros con signo y esto es más natural en los términos de cómo piensan las personas, y (2) el uso de enteros sin signo da como resultado muchos problemas fáciles de crear pero difíciles de depurar, como el desbordamiento aritmético de enteros y la pérdida de bits significativos al convertir entre tipos firmados y no firmados. Si restas erróneamente 1 de 0 usando enteros con signo, a menudo hace que tu progtwig se cuelgue más rápido y hace que sea más fácil encontrar el error que si se ajusta a 2 ^ 32 – 1, y los comstackdores y herramientas de análisis estático y los controles de tiempo de ejecución tienen que supongamos que sabe lo que está haciendo desde que eligió usar aritmética sin signo. Además, los números negativos como -1 a menudo pueden representar algo útil, como un campo que se ignora / se predetermina / no se establece, mientras que si se está utilizando sin firmar, se debe reservar un valor especial como 2 ^ 32 – 1 o algo similar.

Hace mucho tiempo, cuando la memoria era limitada y los procesadores no funcionaban automáticamente en 64 bits a la vez, cada bit contaba mucho más, por lo que tener contraseñas o cortos firmados vs no firmados realmente importaba mucho más a menudo y obviamente era la decisión de diseño correcta. Hoy solo usar un int firmado es más que suficiente en casi todos los casos de progtwigción normales, y si su progtwig realmente necesita usar valores mayores que 2 ^ 31 – 1, a menudo solo quiere un largo de todos modos. Una vez que te adentras en el territorio del uso de los largos, es aún más difícil encontrar una razón por la que realmente no puedas cumplir con 2 ^ 63 – 1 enteros positivos. Cada vez que vayamos a procesadores de 128 bits, será menos problemático.

Porque el tipo unsigned es puro mal.

El hecho de que en C unsigned - int produce unsigned es aún más malo.

Aquí hay una instantánea del problema que me quemó más de una vez:

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

¿Ya has notado el error? Confieso que solo lo vi después de entrar con el depurador.

Como n es de tipo sin signo size_t la expresión completa n - (rays.size() - 1) / 2 evalúa como unsigned . Esa expresión está destinada a ser una posición firmada del n ° rayo del medio: el primer rayo desde el medio del lado izquierdo tendría la posición -1, el 1er a la derecha tendría la posición +1, etc. Después de tomar el valor de abs y multiplicar por el ángulo delta , obtendría el ángulo entre n th ray y el del medio.

Desafortunadamente para mí, la expresión anterior contenía el mal unsigned y en lugar de evaluar, por ejemplo, -1, se evaluó a 2 ^ 32-1. La conversión posterior a double sellado el error.

Después de un error o dos causados ​​por el uso indebido de la aritmética unsigned uno tiene que empezar a preguntarse si el bit extra que uno obtiene vale la pena el problema adicional. Estoy intentando, en la medida de lo posible, evitar el uso de tipos unsigned en aritmética, aunque todavía lo utilizo para operaciones no aritméticas, como las máscaras binarias.

Su pregunta es “¿Por qué Java no admite ints sin firmar”?

Y mi respuesta a su pregunta es que Java quiere que todos sus tipos primitivos: byte , char , short , int y long sean tratados como byte , word , dword y qword respectivamente, exactamente como en el ensamblaje, y los operadores de Java están firmados operaciones en todos sus tipos primitivos a excepción de char , pero solo en char son unsigned 16 bit solamente.

Entonces, los métodos estáticos suponen que son las operaciones sin signo también para 32 y 64 bits.

Necesitas la clase final, cuyos métodos estáticos se pueden llamar para las operaciones sin firmar .

Puede crear esta clase final, llamarla cualquiera que sea el nombre que desee e implementar sus métodos estáticos.

Si no tiene idea acerca de cómo implementar los métodos estáticos, este enlace puede ayudarlo.

En mi opinión, Java no es para nada similar a C ++, si no admite tipos sin firma ni sobrecarga del operador, entonces creo que Java debe tratarse como un lenguaje completamente diferente de C ++ y de C.

Por cierto, también es completamente diferente en el nombre de los idiomas.

Por lo tanto, no recomiendo en Java que escriba un código similar a C y no recomiendo escribir código similar a C ++, porque en Java no podrá hacer lo que quiera hacer a continuación en C ++. es decir, el código no seguirá siendo C ++ en absoluto y para mí es malo codificar así, para cambiar el estilo en el medio.

Recomiendo escribir y utilizar métodos estáticos también para las operaciones firmadas, por lo que no se ve en la combinación de códigos de operadores y métodos estáticos para las operaciones firmadas y no firmadas, a menos que solo necesite operaciones firmadas en el código, y está bien use solo los operadores.

También recomiendo evitar el uso de tipos de primitiva corta , larga y larga y, en su lugar, usar palabra , dword y qword , y se trata de llamar a los métodos estáticos para operaciones sin firmar y / o operaciones firmadas en lugar de utilizar operadores.

Si está a punto de hacer operaciones firmadas solamente y usar los operadores solo en el código, entonces está bien utilizar estos tipos primitivos cortos , int y largos .

En realidad, word , dword y qword no existen en el idioma, pero puedes crear nuevas clases para cada uno y la implementación de cada uno debería ser muy fácil:

La palabra de clase contiene el tipo primitivo solo corto , la clase dword contiene el tipo primitivo int solo y la clase qword contiene el tipo primitivo solo largo . Ahora todos los métodos sin firmar y firmados como estáticos o no como su elección, puede implementar en cada clase, es decir, todas las operaciones de 16 bits sin firmar y firmadas dando nombres de significado en la clase de palabra , todas las operaciones de 32 bits tanto sin firmar como se firma dando nombres de significado en la clase dword y todas las operaciones de 64 bits tanto sin firmar como firmadas dando nombres de significado en la clase qword .

Si no te gusta dar demasiados nombres diferentes para cada método, siempre puedes usar la sobrecarga en Java, ¡es bueno saber que Java no eliminó eso también!

Si desea métodos en lugar de operadores para operaciones y métodos firmados de 8 bits para operaciones sin firmar de 8 bits que no tienen operadores, puede crear la clase Byte (tenga en cuenta que la primera letra ‘B’ es mayúscula, por lo que esta no es byte de tipo primitivo) e implementar los métodos en esta clase.

Acerca de pasar por valor y pasar por referencia:

Si no estoy equivocado, como en C #, los objetos primitivos se pasan naturalmente por valor, pero los objetos de clase se pasan naturalmente por referencia, lo que significa que los objetos de tipo Byte , word , dword y qword se pasarán por referencia y no por valor por defecto. Desearía que Java tuviera objetos struct como C #, por lo que todos los bytes , palabras , dword y qword podrían implementarse como struct en lugar de class , por lo que se pasaban por valor y no por referencia de forma predeterminada, como cualquier objeto struct en C # , al igual que los tipos primitivos, pasan por valor y no por referencia de manera predeterminada, pero dado que Java es peor que C # y tenemos que lidiar con eso, solo hay clases e interfaces, que se pasan por referencia y no por valor por defecto. Entonces, si quiere pasar objetos Byte , word , dword y qword por valor y no por referencia, como cualquier otro objeto de clase en Java y también en C #, simplemente tendrá que usar el constructor de copia y listo .

Esa es la única solución en la que puedo pensar. Solo desearía poder escribir los tipos primitivos en word, dword y qword, pero Java no admite typedef ni usa nada, a diferencia de C # que admite el uso , que es equivalente al typedef de C.

Sobre salida:

Para la misma secuencia de bits , puede imprimirlos de muchas maneras: como binario, como decimal (como el significado de% u en C printf), como octal (como el significado de% o en C printf), como hexadecimal (como el significado de% x en C printf) y como entero (como el significado del% d en C printf).

Tenga en cuenta que C printf no conoce el tipo de variables que se pasan como parámetros a la función, por lo que printf conoce el tipo de cada variable solo desde el objeto char * pasado al primer parámetro de la función.

Por lo tanto, en cada una de las clases: Byte , word , dword y qword , puede implementar el método de impresión y obtener la funcionalidad de printf, aunque el tipo primitivo de la clase esté firmado, aún puede imprimirlo sin firmar siguiendo algún algoritmo relacionado con operaciones lógicas y de cambio para que los dígitos se impriman en la salida.

Lamentablemente, el enlace que te di no muestra cómo implementar estos métodos de impresión, pero estoy seguro de que puedes buscar los algoritmos que necesitas para implementar estos métodos de impresión.

Eso es todo lo que puedo responder a tu pregunta y sugerirte.

Puedo pensar en un desafortunado efecto secundario. En las bases de datos incrustadas de Java, la cantidad de identificadores que puede tener con un campo de identificación de 32 bits es de 2 ^ 31, no de 2 ^ 32 (~ 2billion, no ~ 4billion).

La razón en mi humilde opinión es porque son / eran demasiado vagos para implementar / corregir ese error. Sugiriendo que los progtwigdores de C / C ++ no entienden unsigned, structure, union, bit flag … Es simplemente descabellado.

Ether estabas hablando con un progtwigdor básico / bash / java a punto de comenzar a progtwigr a la C, sin ningún conocimiento real de este lenguaje o simplemente estás hablando de tu propia mente. 😉

cuando tratas todos los días en formato de archivo o hardware, comienzas a cuestionar, ¿qué demonios estaban pensando?

Un buen ejemplo aquí sería tratar de usar un byte sin signo como un bucle de auto rotación. Para aquellos de ustedes que no entienden la última oración, ¿cómo diablos te llamas un progtwigdor?

stream continua