¿Por qué Java no incluye soporte para enteros sin signo?
Me parece 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 autodocumentación, ya que indican que el valor que se suponía que debía tener el int sin signo nunca debería ser 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?
java
language-design
unsigned
integer
dsimcha
fuente
fuente
byte
no puedan dar un140
nivel de gris directo , sino el-116
que necesita& 0xff
para obtener el valor correcto.Respuestas:
Esto es de una entrevista con Gosling y otros , sobre la simplicidad:
fuente
Leyendo entre líneas, creo que la lógica era algo así:
Sobre todo, diría que fue una decisión razonable. Posiblemente, tendría:
Aún así, con un poco de kludging, las operaciones en valores sin signo de hasta 32 bits no son tan malas, y la mayoría de las personas no necesitan división o comparación de 64 bits sin signo.
fuente
short
se usa: los algoritmos defltate / gzip / inflate son de 16 bits y dependen en gran medida de los cortos ... o al menosshort[]
[es cierto que son nativos, pero Java implica que el algoritmo transporta terrabytes de datos]. El último (short[]
) tiene una ventaja significativaint[]
ya que requiere dos veces menos memoria y menos memoria = mejores propiedades de almacenamiento en caché, un rendimiento mucho mejor.Esta es una pregunta anterior y Pat mencionó brevemente char, solo pensé que debería ampliar esto para otros que lo verán más adelante. Echemos un vistazo más de cerca a los tipos primitivos de Java:
byte
- entero con signo de 8 bitsshort
- entero de 16 bits con signoint
- entero con signo de 32 bitslong
- entero con signo de 64 bitschar
- Carácter de 16 bits (entero sin signo)Aunque
char
no es compatible con launsigned
aritmética, esencialmente puede tratarse como ununsigned
número entero. Tendría que volver a convertir explícitamente las operaciones aritméticaschar
, pero le proporciona una forma de especificarunsigned
números.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. También me gustaría haber visto un byte sin firmar, pero supongo que duplicar el costo de la memoria y usar char es una opción viable.
Editar
Con JDK8 hay nuevas API para
Long
yInteger
que proporcionan métodos auxiliares al tratarlong
yint
valores como valores sin signo.compareUnsigned
divideUnsigned
parseUnsignedInt
parseUnsignedLong
remainderUnsigned
toUnsignedLong
toUnsignedString
Además, Guava proporciona una serie de métodos auxiliares para hacer cosas similares en los tipos enteros, lo que ayuda a cerrar la brecha que deja la falta de soporte nativo para los
unsigned
enteros.fuente
char
es demasiado pequeño para admitir lalong
aritmética, por ejemplo.Java tiene tipos sin signo, o al menos uno: char es un corto sin signo. Entonces, cualquier excusa que arroje Gosling es en realidad solo su ignorancia por qué no hay otros tipos sin firmar.
También tipos cortos: los cortos se usan todo el tiempo para multimedia. La razón es que puede ajustar 2 muestras en un solo largo de 32 bits sin signo y vectorizar muchas operaciones. Lo mismo con datos de 8 bits y byte sin signo. Puede colocar 4 u 8 muestras en un registro para vectorizar.
fuente
char
para cualquier cosa que no sean personajes.Tan pronto como enteros sin signo firmados y se mezclan en una expresión las cosas comienzan a causar problemas y es probable que se pierda información. Restringir Java a ints firmados solo aclara las cosas. Me alegro de no tener que preocuparme por todo el negocio firmado / no firmado, aunque a veces extraño el octavo bit en un byte.
fuente
static_cast
mucho para mezclarlos. De hecho es desordenado.byte
sido firmado como lo fue en Pascal.& 0xFF
cada promoción de byte a int hace que el código sea aún más desordenado.http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html
Este tipo dice porque el estándar C define operaciones que involucran entradas sin firmar y firmadas para ser tratadas como sin firmar. Esto podría hacer que los enteros con signo negativo se conviertan en un gran int sin signo, lo que podría causar errores.
fuente
-1
cualquier cantidad sin signo, incluso cero.-1
edad "desconocida" (como sugiere el artículo) es uno de los ejemplos clásicos de "código de olor" . Por ejemplo, si desea calcular "¿cuánto Alice es mayor que Bob?", Y A = 25 y B = -1, obtendrá una respuesta±26
que simplemente es incorrecta. El manejo adecuado de valores desconocidos es algún tipo deOption<TArg>
cuándoSome(25) - None
volveríaNone
.Creo que Java está bien como está, agregarlo sin firmar lo complicaría sin mucha ganancia. Incluso con el modelo entero simplificado, la mayoría de los programadores de Java no saben cómo se comportan los tipos numéricos básicos: solo lea el libro Java Puzzlers para ver qué conceptos erróneos podría tener.
En cuanto a consejos prácticos:
Si sus valores son de tamaño algo arbitrario y no encajan
int
, úseloslong
. Si no encajan enlong
usoBigInteger
.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, excepto por división, comparación, desplazamiento a la derecha y fundición.Consulte también esta respuesta sobre "portar un generador de números aleatorios de C a Java".
fuente
>>
y>>>
para firmado y sin firmar, respectivamente. Desplazarse a la izquierda no es problema.>>>
no funciona parashort
ybyte
. Por ejemplo,(byte)0xff>>>1
rendimientos en0x7fffffff
lugar de0x7f
. Otro ejemplo:byte b=(byte)0xff; b>>>=1;
resultará enb==(byte)0xff
. Por supuesto que puede hacerlo,b=(byte)(b & 0xff >> 1);
pero esto agrega una operación más (bit a bit &).Con JDK8 tiene algún soporte para ellos.
Todavía podemos ver soporte completo de tipos sin firmar en Java a pesar de las preocupaciones de Gosling.
fuente
Sé que esta publicación es demasiado antigua; sin embargo, para su interés, en Java 8 y versiones posteriores, puede usar el
int
tipo de datos para representar un número entero de 32 bits sin signo, que tiene un valor mínimo de 0 y un valor máximo de 2 32 −1. Use laInteger
clase para usarint
el tipo de datos como un entero sin signo y se han agregado métodos estáticos comocompareUnsigned()
,divideUnsigned()
etc. , a laInteger
clase para admitir las operaciones aritméticas para enteros sin signo.fuente
He escuchado historias de que se incluirían cerca de la versión original de Java. Oak fue el precursor de Java, y en algunos documentos de especificaciones se mencionaron valores firmados. Desafortunadamente, estos nunca llegaron al lenguaje Java. Hasta donde alguien ha podido darse cuenta de que simplemente no se implementaron, probablemente debido a una limitación de tiempo.
fuente
char
) se dejaron de lado porque los diseñadores pensaron que era una mala idea ... dados los objetivos del lenguaje.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 programas que usan enteros sin signo pueden funcionar igual de bien con enteros con signo y esto es más natural en términos de cómo piensan las personas y (2) el uso de enteros sin signo resulta en 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 con signo y sin signo. Si por error resta 1 de 0 usando enteros con signo, a menudo hace que su programa se bloquee más rápidamente y hace que sea más fácil encontrar el error que si se ajusta a 2 ^ 32 - 1, y los compiladores y las herramientas de análisis estático y las comprobaciones de tiempo de ejecución tienen que suponga que sabe lo que está haciendo ya que eligió usar aritmética sin signo. También,
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 bytes o cortos firmados vs no firmados realmente importaba mucho más a menudo y obviamente era la decisión de diseño correcta. Hoy en día, solo usar un int firmado es más que suficiente en casi todos los casos de programación regulares, y si su programa realmente necesita usar valores mayores que 2 ^ 31 - 1, a menudo de todos modos solo desea un largo. Una vez que estás en el territorio del uso de largos, es aún más difícil encontrar una razón por la que realmente no puedas sobrevivir con 2 ^ 63-1 enteros positivos. Siempre que vayamos a procesadores de 128 bits, será un problema aún menor.
fuente
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 se traten como byte , word , dword y qword respectivamente, exactamente como en el ensamblado, y los operadores Java están firmados operaciones en todos sus tipos primitivos excepto char , pero solo en char son solo 16 bits sin signo.
Por lo tanto, los métodos estáticos suponen ser las operaciones sin firmar también para 32 y 64 bits.
Necesita la clase final, cuyos métodos estáticos se pueden llamar para los no firmados operaciones .
Puedes crear esta clase final, llamarla como quieras e implementar sus métodos estáticos.
Si no tiene idea de cómo implementar los métodos estáticos, entonces este enlace puede ayudarlo.
En mi opinión, Java no es similar a C ++ en absoluto , si no admite tipos sin signo ni sobrecarga de operadores, por lo que creo que Java debería tratarse como un lenguaje completamente diferente tanto de C ++ como de C.
Por cierto, también es completamente diferente en el nombre de los idiomas.
Por lo tanto, no recomiendo en Java escribir código similar a C y no recomiendo escribir código similar a C ++ en absoluto, porque entonces en Java no podrá hacer lo que quiere hacer a continuación en C ++, es decir, el código no seguirá siendo C ++ como para nada y para mí es malo codificar así, cambiar el estilo en el medio.
Recomiendo escribir y usar métodos estáticos también para las operaciones firmadas, para que no vea en la combinación de códigos de operadores y métodos estáticos tanto para operaciones firmadas como 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 primitivos cortos , int y largos , y usar word , dword y qword respectivamente, y se trata de llamar a los métodos estáticos para operaciones sin signo y / o operaciones con signo en lugar de usar operadores.
Si está a punto de realizar operaciones firmadas únicamente y usa los operadores solo en el código, entonces está bien usar estos tipos primitivos short , int y long .
En realidad la palabra , dword y QWord hacer no existe en la lengua, pero se puede crear nueva clase de cada uno y la implementación de cada uno debe ser muy fácil:
La palabra de clase mantiene el tipo primitivo solo corto , la clase dword contiene el tipo primitivo int solamente y la clase qword solo mantiene el tipo primitivo largo . Ahora, todos los métodos sin signo y con signo como estáticos o no como su elección, puede implementar en cada clase, es decir, todas las operaciones de 16 bits sin signo y firmadas dando nombres de significado en la clase de palabra , todas las operaciones de 32 bits sin signo y firmado dando nombres de significado en la clase dword y todas las operaciones de 64 bits sin signo y firmadas dando nombres de significado en la clase qword .
Si no le gusta dar muchos nombres diferentes para cada método, siempre puede usar la sobrecarga en Java, ¡es bueno leer que Java no eliminó eso también!
Si desea métodos en lugar de operadores para operaciones con signo de 8 bits y métodos para operaciones sin signo de 8 bits que no tienen ningún operador, puede crear la clase Byte (tenga en cuenta que la primera letra 'B' es mayúscula, por lo que este no es el byte de tipo primitivo ) e implementar los métodos en esta clase.
Acerca de pasar por valor y pasar por referencia:
Si no me equivoco, como en C #, los objetos primitivos se pasan por valor de forma natural, pero los objetos de clase se pasan por referencia de forma natural, 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 , word , dword y qword podrían implementarse para ser struct en lugar de class, de manera predeterminada, se pasaron por valor y no por referencia de manera predeterminada, como cualquier objeto de estructura en C #, como los tipos primitivos, se pasan por valor y no por referencia de manera predeterminada, sino porque Java es peor que C # y tenemos para lidiar con eso, solo hay clases e interfaces, que se pasan por referencia y no por valor por defecto. Entonces, si desea pasar los 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 para word, dword y qword, pero Java no admite typedef ni usa en absoluto, 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 de% d en C printf).
Tenga en cuenta que C printf no conoce el tipo de las 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.
Entonces, 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 como no firmado siguiendo algunos algoritmos que involucran operaciones lógicas y de cambio para obtener los dígitos para imprimir en la salida.
Desafortunadamente, el enlace que le di no muestra cómo implementar estos métodos de impresión, pero estoy seguro de que puede buscar en Google los algoritmos que necesita para implementar estos métodos de impresión.
Eso es todo lo que puedo responder a su pregunta y sugerirle.
fuente
Porque el
unsigned
tipo es puro mal.El hecho de que en C
unsigned - int
produceunsigned
es aún más malvado.Aquí hay una instantánea del problema que me quemó más de una vez:
¿Ya has notado el error? Confieso que solo lo vi después de intervenir con el depurador.
Como
n
es de tipo sin signo,size_t
toda la expresión sen - (rays.size() - 1) / 2
evalúa comounsigned
. Se pretende que esa expresión sea una posición firmada deln
rayo th desde el medio: el 1er rayo del medio en el lado izquierdo tendría la posición -1, el 1er a la derecha tendría la posición +1, etc. tomando el valor de abs y multiplicándolo por eldelta
ángulo obtendría el ángulo entre eln
rayo th y el del medio.Desafortunadamente para mí, la expresión anterior contenía el mal sin signo y, en lugar de evaluar, digamos -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 mal uso de la
unsigned
aritmética, uno tiene que comenzar a preguntarse si el bit extra que se obtiene vale la pena. Estoy tratando, en la medida de lo posible, de evitar cualquier uso deunsigned
tipos en aritmética, aunque todavía lo uso para operaciones no aritméticas como las máscaras binarias.fuente
unsigned
se convierteint
en cada operación, ¿de qué sirveunsigned
? No tendrá ninguna funcionalidad distinguible deshort
. Y siint
solo realiza la conversión en operaciones mixtas, comounsigned+int
ounsigned+float
, entonces todavía tiene el problema de((unsigned)25-(unsigned)30)*1.0 > 0
, que es una causa importante deunsigned
errores relacionados.exit(1);
realmente vale la pena? ¿No es realmente valioso poder abrir archivos grandes la seguridad que los programadores java menos experimentados no van a estropearunsigned
?n - (rays.size() - 1) / 2
. Siempre debe poner entre paréntesis a los operadores binarios porque el lector del código no debería tener que asumir nada sobre el orden de las operaciones en un programa de computadora. El hecho de que convencionalmente digamos a + b c = a + (b c) no significa que pueda asumir esto al leer el código. Además, el cálculo debe definirse fuera del ciclo para que pueda probarse sin el ciclo presente. Esto es un error al no asegurarse de que sus tipos se alineen en lugar de un problema de enteros sin signo. En C, depende de usted asegurarse de que sus tipos se alineen.Hay algunas gemas en la especificación 'C' que Java dejó caer por razones pragmáticas, pero que lentamente están retrocediendo con la demanda del desarrollador (cierres, etc.).
Menciono una primera porque está relacionada con esta discusión; La adherencia de los valores de puntero a la aritmética de enteros sin signo. Y, en relación con este tema del hilo, la dificultad de mantener la semántica sin signo en el mundo firmado de Java.
Supongo que si uno obtuviera un alter ego de Dennis Ritchie para asesorar al equipo de diseño de Gosling, habría sugerido dar a Signed un "cero en el infinito", de modo que todas las solicitudes de desplazamiento de dirección agreguen primero su TAMAÑO DE ANILLO ALGEBRAICO para obviar los valores negativos.
De esa manera, cualquier desplazamiento lanzado en la matriz nunca puede generar un SEGFAULT. Por ejemplo, en una clase encapsulada a la que llamo RingArray de dobles que necesita un comportamiento sin signo, en el contexto del "ciclo de rotación automática":
El RingArray anterior nunca '' obtendría '' de un índice negativo, incluso si un solicitante malintencionado lo intentara. Recuerde, también hay muchas solicitudes legítimas para solicitar valores de índice anteriores (negativos).
NB: El% módulo externo desreferencia las solicitudes legítimas, mientras que el módulo% interno oculta la malicia evidente de los negativos más negativos que el módulo. Si esto alguna vez apareciera en un Java + .. + 9 || 8 + .. + espec., Entonces el problema se convertiría realmente en un 'programador que no puede "auto rotar" FALLA ".
Estoy seguro de que la llamada 'deficiencia' de Java sin signo int se puede compensar con la línea de arriba.
PD: Solo para dar contexto al mantenimiento de RingArray anterior, aquí hay una operación candidata 'set' para que coincida con la operación del elemento 'get' anterior:
fuente
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 2 ^ 31, no 2 ^ 32 (~ 2 billones, no ~ 4 billones).
fuente
La razón en mi humilde opinión es porque son / eran demasiado flojos para implementar / corregir ese error. Sugerir que los programadores de C / C ++ no entiendan sin firmar, estructura, unión, indicador de bit ... es simplemente absurdo.
Ether estabas hablando con un programador básico / bash / java a punto de comenzar a programar a la C, sin ningún conocimiento real de este lenguaje o simplemente estás hablando por tu propia cuenta. ;)
cuando tratas todos los días en formato, ya sea desde un 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 giratorio automático. Para aquellos de ustedes que no entienden la última oración, cómo demonios se llaman programadores.
corriente continua
fuente