¿Por qué Java no admite ints sin firmar?

374

¿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?

dsimcha
fuente
137
No lo sé, pero me molesta muchísimo; Por ejemplo, es mucho más difícil escribir código de red de esta manera.
Tamas Czinege
20
Desearía que solo hubiera dos tipos en el idioma / base de datos / ... mundo: número y cadena :)
Liao
55
Escribir código de red no es mucho más difícil. Por cierto InputStream.read (), devuelve un byte sin firmar, no uno firmado, por ejemplo, por lo que el ejemplo de red es una confusión en mi humilde opinión. Lo único confuso es que asume que escribir un valor firmado es diferente de escribir uno sin firmar. es decir, si realmente no sabes lo que está sucediendo a nivel de byte.
Peter Lawrey
19
@ZachSaw: también hice una doble toma cuando vi a un diseñador de idiomas hacer esa cita. No hay nada más simple que un entero sin signo. Los enteros firmados son complicados. Particularmente cuando consideras el giro de bits en el nivel del transistor. ¿Y cómo cambia un número entero con signo? Tuve que concluir que el diseñador de Java tiene un problema grave para entender la lógica booleana.
PP.
8
Para mí, se hace más difícil hacer cualquier procesamiento de imágenes con imágenes que byteno puedan dar un 140nivel de gris directo , sino el -116que necesita & 0xffpara obtener el valor correcto.
Matthieu

Respuestas:

193

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

Gosling: Para mí, como diseñador de idiomas, que realmente no considero en estos días, lo que "simple" realmente terminó significando podría esperar que J. Random Developer tenga la especificación en su cabeza. Esa definición dice que, por ejemplo, Java no lo es, y de hecho muchos de estos lenguajes terminan con muchos casos de esquina, cosas que nadie entiende realmente. Pregunte a cualquier desarrollador de C sobre unsigned, y muy pronto descubrirá que casi ningún desarrollador de C realmente comprende lo que sucede con unsigned, qué es la aritmética sin signo. Cosas como esas hicieron que C. sea complejo. La parte del lenguaje de Java es, creo, bastante simple. Las bibliotecas que tienes que buscar.

Uri
fuente
222
Voy a tener que estar en desacuerdo con Gosling aquí con un ejemplo específico (de CLR no menos). ¿Qué es más confuso si le da a una matriz un valor de longitud entero con signo o una longitud sin signo? Es imposible que una matriz tenga una longitud negativa, sin embargo, nuestra API indica que es posible.
JaredPar
18
El argumento de hacer que Java sea simple es parte de lo que nos metió en todo el lío con la falta de plantillas que finalmente trajeron al lenguaje porque las alternativas eran muy engorrosas. Sin embargo, creo que uno podría admitir entradas no firmadas con una clase apropiada, no necesita prims
Uri
59
Si Java necesita enteros sin signo porque los índices de matriz no pueden ser negativos, entonces también necesita subranges (a la Pascal) porque un índice de matriz no puede ser mayor que el tamaño de la matriz.
Wayne Conrad el
81
De acuerdo, él acaba de decir las ventajas de no tener tipos sin firmar. Ahora vamos a contar las desventajas ...
Moshe Revah
83
Prefiero la simplicidad del código sobre la simplicidad del lenguaje. Por eso odio Java.
Pijusn
50

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

  • en general, los diseñadores de Java querían simplificar el repertorio de tipos de datos disponibles
  • Para fines cotidianos, sentían que la necesidad más común era de tipos de datos firmados
  • para implementar ciertos algoritmos, a veces se necesita aritmética sin signo, pero el tipo de programadores que implementarían dichos algoritmos también tendrían el conocimiento para "trabajar" haciendo aritmética sin signo con tipos de datos con signo

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

  • hecho byte sin signo, o al menos ha proporcionado alternativas con signo / sin signo, posiblemente con diferentes nombres, para este tipo de datos (hacer que esté firmado es bueno para la coherencia, pero ¿cuándo necesita un byte firmado?)
  • eliminado 'corto' (¿cuándo usó la aritmética con signo de 16 bits por última vez?)

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.

Neil Coffey
fuente
2
También me encantaría tener bytes sin signo, pero sospecho que la ventaja de una coherencia completa entre los tipos de enteros supera la conveniencia que traerían los bytes sin signo.
Alan Moore
64
"Para propósitos cotidianos, sintieron que la necesidad más común era para los tipos de datos firmados". En mi código C ++, más de una vez me encuentro pensando "¿Por qué demonios estoy usando un entero con signo aquí en lugar de uno sin signo?". Tengo la sensación de que "firmado" es la excepción más que la regla (por supuesto, depende del dominio, pero hay una razón por la cual los enteros positivos se llaman números naturales ;-)).
Luc Touraille
15
pulgar hacia arriba para la llamada de bytes sin firmar, al hacer el procesamiento de imágenes, suponiendo que los bytes no estén firmados (como debería ser), me hizo pasar horas depurando.
Helin Wang
77
te sorprendería la frecuencia con la que shortse usa: los algoritmos defltate / gzip / inflate son de 16 bits y dependen en gran medida de los cortos ... o al menos short[][es cierto que son nativos, pero Java implica que el algoritmo transporta terrabytes de datos]. El último ( short[]) tiene una ventaja significativa int[]ya que requiere dos veces menos memoria y menos memoria = mejores propiedades de almacenamiento en caché, un rendimiento mucho mejor.
Bestsss
8
Aunque en una aplicación en particular, debe medir si el uso de cortos le brinda un mejor rendimiento en lugar de asumir que es cierto. Es posible que la jiggery-pokery adicional requerida para manipular cortos en lugar de ints (que generalmente es el tipo que al procesador 'le gusta usar') en realidad podría ser perjudicial para el rendimiento en una aplicación en particular. No siempre, pero debes probar, no asumir.
Neil Coffey
19

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 bits

short - entero de 16 bits con signo

int - entero con signo de 32 bits

long - entero con signo de 64 bits

char - Carácter de 16 bits (entero sin signo)

Aunque charno es compatible con la unsignedaritmética, esencialmente puede tratarse como un unsignednúmero entero. Tendría que volver a convertir explícitamente las operaciones aritméticas char, pero le proporciona una forma de especificar unsignednúmeros.

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. 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 Longy Integerque proporcionan métodos auxiliares al tratar longy intvalores 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 unsignedenteros.

Jyro117
fuente
2
Pero, sin embargo, chares demasiado pequeño para admitir la longaritmética, por ejemplo.
3
Esto podría ser una desventaja de Java
Con la esperanza de que admitan valores sin signo para bytes. Hace las cosas más fáciles.
blendz
15

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.

palmadita
fuente
37
Sí, estoy seguro de que Gosling es muy ignorante sobre Java en comparación con usted.
jakeboxer
¿Permite Java que la aritmética se realice directamente en cantidades de bytes sin signo, o siempre se promueven los valores? Tener un tipo sin signo para el almacenamiento, pero siempre realizar operaciones aritméticas en un tipo con signo que sea lo suficientemente grande como para acomodarlo funciona semánticamente, pero causaría que las operaciones en tipos sin signo que fueran del mismo tamaño que los enteros "normales" sean más caras.
supercat
2
Es un mal estilo charpara cualquier cosa que no sean personajes.
starblue
55
@starblue Por supuesto que sí, pero es un truco para sortear una limitación del idioma
Básico
14

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.

Bombe
fuente
12
En cuanto a la mezcla con signo / sin signo: podría tener tipos sin signo, pero no permitir la mezcla (o requerir conversiones explícitas). Aún así, no está claro si es necesario.
sleske
2
En C ++ tienes que rociar static_castmucho para mezclarlos. De hecho es desordenado.
Raedwald
44
El octavo bit está allí, solo intenta ocultarse como el signo.
Starblue
Las cosas solo se complican con los tipos de 32 bits o más. No veo ninguna razón por la que Java no debería haber bytesido firmado como lo fue en Pascal.
supercat
12
Ven a verme cuando tengas problemas con el procesamiento de imágenes en Java, donde esperas que los bytes no estén firmados. Entonces sabrás que & 0xFFcada promoción de byte a int hace que el código sea aún más desordenado.
bit2shift
12

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.

akatakritos
fuente
34
Los enteros con signo Java también giran. No entiendo tu punto.
foo
8
@foo: Los enteros firmados tienen que crecer antes de que causen problemas. Por el contrario, en C, uno puede tener problemas para comparar cualquier número entero negativo, incluso, con -1cualquier cantidad sin signo, incluso cero.
supercat
Es una lástima que Java no podría haber incluido tipos sin signo, pero con un conjunto limitado de conversiones y operadores mixtos (algo análogo a la forma en que en C se puede agregar 5 a un puntero, pero no se puede comparar un puntero a 5) . La idea de que el uso de un operador en tipos mixtos cuando existe una conversión implícita, debe forzar el uso implícito de esa conversión (y usar el tipo consecuente como el tipo de resultado) se encuentra en el corazón de muchas decisiones de diseño dudosas tanto en .NET como en Java.
Supercat
44
No despotricar sobre su respuesta, pero tener una -1edad "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 ±26que simplemente es incorrecta. El manejo adecuado de valores desconocidos es algún tipo de Option<TArg>cuándo Some(25) - Nonevolvería None.
bytebuster
11

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, úselos long. Si no encajan en longuso 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/ bytey 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".

starblue
fuente
55
Sí, para desplazarse a la derecha debe elegir entre >>y >>>para firmado y sin firmar, respectivamente. Desplazarse a la izquierda no es problema.
Starblue
1
@starblue En realidad >>>no funciona para shorty byte. Por ejemplo, (byte)0xff>>>1rendimientos en 0x7ffffffflugar de 0x7f. Otro ejemplo: byte b=(byte)0xff; b>>>=1;resultará en b==(byte)0xff. Por supuesto que puede hacerlo, b=(byte)(b & 0xff >> 1);pero esto agrega una operación más (bit a bit &).
CITBL
77
"... Incluso con el modelo simplificado, la mayoría de los programadores de Java no saben cómo se comportan los tipos numéricos básicos ..." Algo en mí simplemente resiente un lenguaje dirigido al mínimo común denominador.
Básico
La línea de apertura en su respuesta, sobre más complicaciones y poca ganancia, es precisamente lo que elaboré
Nayuki
1
@Nayuki Tu artículo es realmente agradable. Solo una pequeña observación, usaría la adición de 0x80000000 para operadores de comparación en lugar de XOR, porque explica por qué funciona, cambia la región contigua donde la comparación ocurre de -MAXINT a 0. Bitwise su efecto es exactamente el mismo.
starblue
6

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.

John Hascall
fuente
12
también conocido como "Así que la gente realmente lo usa y nos equivocamos al no incluirlo para empezar", pero todavía no confiamos en que los desarrolladores de Java sepan si una variable está firmada o no, por lo que no vamos a implementarla en la VM o como tipos equivalentes a sus primos firmados.
Básico
6

Sé que esta publicación es demasiado antigua; sin embargo, para su interés, en Java 8 y versiones posteriores, puede usar el inttipo 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 la Integerclase para usar intel tipo de datos como un entero sin signo y se han agregado métodos estáticos como compareUnsigned(), divideUnsigned()etc. , a la Integerclase para admitir las operaciones aritméticas para enteros sin signo.

Morteza Adi
fuente
4

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.

Rob Ottaway
fuente
Esto estaría bien ... excepto que la evidencia de la entrevista de Gosling implica que los enteros sin signo (aparte de char) se dejaron de lado porque los diseñadores pensaron que era una mala idea ... dados los objetivos del lenguaje.
Stephen C
Es una buena idea nunca poner demasiado valor en las declaraciones de los testigos oculares, si la evidencia documental también está a la mano.
user7610
4

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.

Jonathan
fuente
2

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
MASM (ensamblador de Microsoft) y Windows definen BYTE, WORD, DWORD, QWORD como tipos sin firmar. Para MASM, SBYTE, SWORD, SDWORD, SQWORD son los tipos firmados.
rcgldr
1

Porque el unsignedtipo es puro mal.

El hecho de que en C unsigned - intproduce unsignedes aún más malvado.

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 intervenir con el depurador.

Como nes de tipo sin signo, size_ttoda la expresión se n - (rays.size() - 1) / 2evalúa como unsigned. Se pretende que esa expresión sea una posición firmada del nrayo 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 el deltaángulo obtendría el ángulo entre el nrayo 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 doublesellado el error.

Después de un error o dos causados ​​por el mal uso de la unsignedaritmé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 de unsignedtipos en aritmética, aunque todavía lo uso para operaciones no aritméticas como las máscaras binarias.

Miguel
fuente
Agregar "unsigned long" a Java sería incómodo. Sin embargo, agregar tipos sin signo más pequeños no debería haber planteado ningún problema. Especialmente los tipos más pequeños que "int" podrían haber sido manejados fácilmente al hacer que se promocionen a "int" de la manera numéricamente obvia, y "unsigned int" podría haberse manejado diciendo que las operaciones que involucran un int firmado y un unsigned int promoverán ambos operandos a "largo". La única situación problemática serían las operaciones que implican una longitud sin signo y una cantidad con signo, ya que no habría ningún tipo capaz de representar todos los valores de ambos operandos.
supercat
@supercat: si unsignedse convierte inten cada operación, ¿de qué sirve unsigned? No tendrá ninguna funcionalidad distinguible de short. Y si intsolo realiza la conversión en operaciones mixtas, como unsigned+into unsigned+float, entonces todavía tiene el problema de ((unsigned)25-(unsigned)30)*1.0 > 0, que es una causa importante de unsignederrores relacionados.
Michael
Muchas operaciones en tipos sin firmar promoverían a "largo". Requerir conversiones explícitas al almacenar el resultado de nuevo en tipos sin signo causaría las mismas molestias que existen con short y byte, pero si el tipo es principalmente un formato de almacenamiento en lugar de un formato de cálculo, eso no debería ser un problema. En cualquier caso, los tipos sin signo más cortos que "int" simplemente deberían poder promocionarse a "int" sin dificultad.
supercat
3
No me gusta esta respuesta porque usa el argumento "los enteros sin signo son malos y no deberían existir porque nunca se pueden firmar". Cualquiera que intente restar de un entero sin signo ya debería saber esto. Y en cuanto a la legibilidad, C no es exactamente conocido por ser fácil de seguir. Además, el argumento (semi) "el bit extra no vale la pena" es muy débil también. ¿El manejo de errores en lugar de 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 estropear unsigned?
yyny
2
Lo único malo que veo en este código es 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.
Dmitry
0

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":

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

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:

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
}
MKhomo
fuente
-2

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).

Mike g
fuente
1
Probablemente esté pensando en matrices y no sea capaz de usar enteros negativos como índices. Probablemente.
SK9
2
Cuando los campos de incremento automático en las bases de datos se desbordan, a menudo se vuelven locos.
Joshua
-8

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

Denis Co
fuente
34
Solo por diversión, busca en Google la frase "bucle giratorio automático". Claramente , Denis Co es la única persona en el mundo digna de llamarse programador :-)
Stephen C
66
Esta respuesta es tan mala que es divertida
Nayuki el