¿Por qué no hay un operador de energía en Java / C ++?

23

Si bien existe dicho operador, **en Python, me preguntaba por qué Java y C ++ tampoco tienen uno.

Es fácil crear uno para las clases que defina en C ++ con sobrecarga del operador (y creo que esto también es posible en Java), pero cuando se habla de tipos primitivos como int, double, etc., deberá usar la biblioteca funcionan como Math.power(y generalmente tienen que lanzar ambos al doble).

Entonces, ¿por qué no definir dicho operador para los tipos primitivos?

RanZilber
fuente
8
En C ++, no podemos crear nuestros propios operadores. Solo puede sobrecargar los operadores existentes.
1
@Mahesh, así puedo crear mi propia clase de Número y sobrecargar ^ operador para ser una potencia. Eso realmente no importa.
22
@RanZilber: Importa porque la precedencia del ^operador no coincide con la precedencia de la exponenciación. Considera la expresión a + b ^ c. En matemáticas, la exponenciación se realiza primero ( b ^ c), luego se agrega la potencia resultante a. En C ++, la adición se realiza primero ( a + b) y luego ^se realiza el operador con c. Entonces, incluso si implementó el ^operador para significar exponenciación, la precedencia sorprenderá a todos.
En silico
2
@RamZilber: ^es un XOR en C ++. Se recomienda que el operador sobrecargado no haga diferente lo que hace un tipo de datos primitivo al usarlo.
44
@RanZilber: Debido a que no es nada intuitivo usar ninguno de esos operadores que mencionas para significar exponenciación. Cuestionaría seriamente la competencia de cualquier programador de C ++ que sobrecargue al ++operador o al !operador et. Alabama. significar exponenciación. Pero de todos modos no puedes, porque los operadores de los que hablas aceptan solo un argumento; La exponenciación requiere dos argumentos.
En silico

Respuestas:

32

En términos generales, los operadores primitivos en C (y por extensión C ++) están diseñados para ser implementados por hardware simple en aproximadamente una sola instrucción. Algo como la exponenciación a menudo requiere soporte de software; entonces no está allí por defecto.

Además, es proporcionado por la biblioteca estándar del idioma en forma de std::pow.

Finalmente, hacer esto para los tipos de datos enteros no tendría mucho sentido, porque la mayoría de los valores incluso pequeños para la exponenciación superan el rango requerido para int, que es hasta 65.535. Claro, podrías hacer esto para dobles y flotantes, pero no para ints, pero ¿por qué hacer que el lenguaje sea inconsistente para una característica raramente utilizada?

Billy ONeal
fuente
44
Si bien estoy de acuerdo con la mayor parte de esto, el hecho de que el operador de módulo no se pueda usar en tipos de punto flotante es inconsistente para los tipos de datos primitivos, pero que probablemente tampoco sería una sola instrucción en cualquier hardware que imagino que ahora prevalece de alguna manera.
@Sion: Al menos en x86, el módulo es una sola instrucción. ( DIVhace división y módulo) Sin embargo, me tienes en el punto de consistencia.
Billy ONeal
@Billy ONeal: ¿Módulo de punto flotante en una sola instrucción? Últimamente no me he movido en la asamblea para saber por mí mismo. Si ese es el caso, entonces el operador del módulo debe hacerse aplicable a los tipos de coma flotante.
3
@DonalFellows: FORTRAN tenía el operador de exponenciación mucho antes de que tuviera algo parecido al soporte bignum.
supercat
1
@DonalFellows: un operador de poder no es tan útil con números enteros como con flotadores, pero para poderes pequeños (especialmente cuadratura) definitivamente podría tener sus usos. Personalmente, me gusta el enfoque de hacer operadores con letras (como lo hace Pascal con divo con FORTRAN .EQ.); Dependiendo de las reglas del espacio en blanco del lenguaje, puede ser posible tener un número arbitrario de operadores sin requerir que sean palabras reservadas.
supercat
41

Esta pregunta es responsable de C ++: Stroustrup, "Diseño y evolución de C ++", trata esto en la sección 11.6.1, pp. 247-250.

Hubo objeciones generales para agregar un nuevo operador. Se agregaría a la tabla de precedencia ya demasiado complicada. Los miembros del grupo de trabajo pensaron que sería muy conveniente tener una función, y en ocasiones querían poder sustituir sus propias funciones.

No había un buen candidato para un operador. ^es exclusivo-o, ​​e ^^invita a la confusión debido a la relación entre &y |y &&y ||. !no era adecuado, ya que habría una tendencia natural a escribir !=para la exponenciación de un valor existente, y eso ya se había tomado. El mejor disponible puede haber sido *^, que aparentemente a nadie le gustó realmente.

Stroustrup lo consideró **nuevamente, pero ya tiene un significado en C: a**pes aveces lo que sea que papunte, y se char ** c;declara ccomo un puntero a apuntar char. La introducción **como un token que significa "declaración de un puntero a puntero a", "multiplicado por lo que señala lo siguiente" (si es un puntero) o "exponenciación" (si va seguido de un número) causó problemas de precedencia. a/b**ptendría que analizar como a/(b**p)si p fuera un número, pero(a/b) * *p si p fuera un puntero, entonces esto debería resolverse en el analizador.

En otras palabras, habría sido posible, pero habría complicado la tabla de precedencia y el analizador, y ambos ya son demasiado complicados.

No sé la historia sobre Java; todo lo que podría hacer sería especular. En cuanto a C, donde comenzó, todos los operadores de C se traducen fácilmente en código de ensamblaje, en parte para simplificar el compilador y en parte para evitar ocultar la funcionalidad que consume mucho tiempo en operadores simples (el hecho de que operator+()y otros pudieran ocultar una gran complejidad y golpes de rendimiento fue uno de las primeras quejas sobre C ++).

David Thornley
fuente
2
Buena respuesta. Supongo que Java trató de simplificar C a este respecto, por lo que nadie quería agregar un nuevo operador. Es una pena que nadie me haya preguntado, seguramente me hubiera gustado *^. : D
maaartinus
1
C fue creado para formatear texto. Fortran fue construido para hacer matemáticas y tenía matemáticas complejas, de poder y de matriz 20 años antes.
Martin Beckett
@ Martin Beckett: ¿Puedes encontrar evidencia de que C se creó para formatear texto? Me parece un lenguaje muy torpe para eso, y lo que he leído sobre el origen de C dice que fue diseñado principalmente para la programación del sistema para Unix.
David Thornley
@DavidThornley: fue diseñado para escribir Unix, pero todos los primeros usos de Unix parecen haber sido formateado de texto, y por el momento tiene una extensa cadena y biblioteca de E / S.
Martin Beckett
66
+1: El significado existente para a**pes el asesino. (Los trucos para solucionar ese problema ... ¡Brr!)
Donal Fellows
13

Yo sospecho que es porque todos los operadores se introduce aumenta la complejidad de la lengua. La barrera de entrada es, por lo tanto, muy alta. Me encuentro usando la exponenciación muy, muy raramente, y estoy más que feliz de usar una llamada de método para hacerlo.

Jon Skeet
fuente
3
Yo usaría x**2y x**3no tan raramente. Y una implementación de pow mágico que el compilador conoce y optimiza para los casos simples estaría bien.
CodesInChaos
2
@CodeInChaos: Sin embargo x * x, y x * x * xno son malos sustitutos del cuadrado y el cubo.
David Thornley
55
@David, no puedes simplemente escribir x*xsi x es una expresión. En el mejor de los casos, el código se vuelve difícil de manejar y, en el peor, más lento o incluso incorrecto. Por lo tanto, necesitaría definir sus propias funciones Square y Cube. E incluso entonces el código sería más feo que usar ** como operador de energía.
CodesInChaos
1
@David Necesito poner paréntesis sí, pero no necesito repetir la expresión varias veces e hincha el código fuente. Lo que reduce mucho la legibilidad. Y la eliminación de subexpresión común solo es posible si el compilador puede garantizar que la expresión esté libre de efectos secundarios. Y al menos .net jitter no es demasiado inteligente en ese sentido.
CodesInChaos
11

El lenguaje Java y los diseñadores de la biblioteca central decidieron relegar la mayoría de las operaciones matemáticas a la clase de Matemáticas . Ver Math.pow () .

¿Por qué? Flexibilidad para priorizar el rendimiento sobre la precisión bit por bit. Iría en contra del resto de las especificaciones del lenguaje decir que el comportamiento de los operadores matemáticos incorporados podría variar de una plataforma a otra, mientras que la clase de Matemáticas establece específicamente que el comportamiento potencialmente sacrifica la precisión por el rendimiento, por lo que el comprador tenga cuidado:

A diferencia de algunos de los métodos numéricos de la clase StrictMath , todas las implementaciones de las funciones equivalentes de la clase Math no están definidas para devolver los mismos resultados bit por bit. Esta relajación permite implementaciones de mejor rendimiento donde no se requiere una reproducibilidad estricta.


fuente
6

La exponenciación fue parte de Fortran desde el principio porque estaba dirigida directamente a la programación científica. Los ingenieros y físicos lo usan a menudo en simulaciones, porque las relaciones de la ley de poder son comunes en la física.

Python también tiene una fuerte presencia en informática científica (por ejemplo, NumPy y SciPy). Eso, junto con su operador de exponenciación, sugiere que también estaba dirigido a la programación científica.

C, Java y C # tienen raíces en la programación del sistema. Quizás esa sea una influencia que mantuvo la exponenciación fuera del grupo de operadores compatibles.

Solo una teoria.

duffymo
fuente
4

C solo definió operadores para operaciones aritméticas comunes accesibles con la ALU. Su objetivo principal era crear una interfaz legible por humanos para el código de ensamblaje.

C ++ no cambió el comportamiento de ningún operador porque quería que toda la base de código escrita en C fuera compatible.

Java hizo lo mismo porque no quería intimidar a los programadores de C ++ existentes.

Tugrul Ates
fuente
Cuando se creó C, la multiplicación y la división no carecían con poca frecuencia de hardware y debían implementarse en software. Sin embargo, C tiene operadores de multiplicación y división.
siride
@siride: Que yo sepa, el PDP-7, la primera computadora en ejecutar Unix, tuvo multiplicación y división de hardware a través de su EAE. Ver: bitsavers.org/pdf/dec/pdp7/F-75_PDP-7userHbk_Jun65.pdf
Tugrul Ates
1

Bueno, porque cada operador que tendría sentido para una potencia ya está en uso. ^ es XOR y ** define un puntero a un puntero. Entonces, en cambio, solo tienen una función que hace lo mismo. (como pow ())

Skyler Saleh
fuente
@RTS: ¿un desarrollador de idiomas realmente está buscando sentido más que eficiencia?
Un buen desarrollador de un lenguaje de programación mira a ambos. No puedo decir nada sobre Java. Pero en c ++ la función pow () se calcula en tiempo de compilación. Y es tan eficiente como los operadores regulares.
@RTS: la pow()función realiza su cálculo en tiempo de ejecución, a menos que tenga un compilador que pueda hacer un plegado constante pow(), lo cual dudo mucho. (Algunos compiladores le dan la opción de utilizar las características intrínsecas del procesador para realizar el cálculo, sin embargo.)
En silico
@ En silico no quise decir que calcula el valor final, quise decir que los compiladores optimizarán la llamada a la función, por lo que solo tiene la ecuación en bruto.
2
@josefx: Claro que es una buena razón. Un single *es un token léxico, ya sea que se use para indirección o multiplicación. Una **exponenciación de significado sería uno o dos tokens léxicos, y realmente no desea que su lexer tenga que presionar la tabla de símbolos para tokenizar.
David Thornley
0

El hecho es que los operadores aritméticos son solo atajos de funciones. (Casi) Todo lo que haces con ellos se puede hacer con una función. Ejemplo:

c = a + b;
// equals
c.set(a.add(b));
// or as free functions
set(c, add(a,b));

Es simplemente más detallado, por lo que no veo nada de malo en usar funciones para realizar 'poder de'.

Xeo
fuente
-1

Suma / resta / negación y multiplicación / división son operadores matemáticos básicos. Si tuvieras que hacer del poder un operador, ¿dónde te detendrías? Operador de raíz cuadrada? Operador de raíz N? Operador de logaritmo?

No puedo hablar por sus creadores, pero puedo decir que creo que sería difícil de manejar y no ortogonal tener tales operadores en el idioma. El número de caracteres no alfanuméricos / en blanco que quedan en el teclado es bastante limitado. Tal como están las cosas, es extraño que haya un operador de módulo en C ++.

Sion Sheevok
fuente
+1 - No veo por qué modes extraño tener como operador. Suele ser una sola instrucción. Es una operación primitiva en enteros. Se usa en casi todas partes en informática. (Implementar cosas como amortiguadores limitados sin mod
apestaría
@Billy ONeal: extraño debido a la inconsistencia entre poder usar con tipos enteros y tipos de punto flotante. Sin embargo, es absolutamente útil y no soñaría con eliminarlo. Solo peculiar es todo.