Por que não há operador de energia em Java / C ++?

23

Embora exista esse operador - **em Python, eu queria saber por que Java e C ++ não têm um também.

É fácil criar um para as classes que você define em C ++ com sobrecarga de operador (e acredito que isso também é possível em Java), mas ao falar sobre tipos primitivos, como int, double e assim por diante, você terá que usar a biblioteca funcionam como Math.power(e geralmente têm que converter os dois para dobrar).

Então - por que não definir esse operador para tipos primitivos?

RanZilber
fonte
8
Em C ++, não podemos criar nossos próprios operadores. Você pode sobrecarregar apenas os operadores existentes.
1
@ Mahesh, para que eu possa criar minha própria classe Number e sobrecarregar ^ operador para ser um poder. Isso realmente não importa.
22
@RanZilber: Importa porque a precedência do ^operador não corresponde à precedência da exponenciação. Considere a expressão a + b ^ c. Em matemática, a exponenciação é realizada primeiro ( b ^ c), depois a potência resultante é adicionada a a. No C ++, a adição é realizada primeiro ( a + b) e depois o ^operador é executado com c. Portanto, mesmo se você implementou o ^operador para significar exponenciação, a precedência surpreenderá a todos.
In silico 03/03
2
@RamZilber - ^é um XOR em C ++. É recomendável que o operador sobrecarregado não faça diferente do que um tipo de dados primitivo faz usando-o.
4
@RanZilber: Porque não é nada intuitivo usar qualquer um desses operadores mencionados como exponenciação. Eu questionaria seriamente a competência de qualquer programador C ++ que sobrecarrega o ++operador ou o !operador et. al. significar exponenciação. Mas você não pode, de qualquer maneira, porque os operadores sobre os quais você fala aceitam apenas um argumento; exponenciação requer dois argumentos.
Em silico

Respostas:

32

De um modo geral, os operadores primitivos em C (e por extensão C ++) são projetados para serem implementáveis ​​por hardware simples em aproximadamente uma única instrução. Algo como exponenciação geralmente requer suporte de software; então não está lá por padrão.

Além disso, é fornecido pela biblioteca padrão do idioma na forma de std::pow.

Finalmente, fazer isso para tipos de dados inteiros não faria muito sentido, porque a maioria dos pequenos valores de exponenciação amplia o intervalo necessário para int, ou seja, 65.535. Claro, você poderia fazer isso para duplas e flutuantes, mas não para ints, mas por que tornar o idioma inconsistente para um recurso raramente usado?

Billy ONeal
fonte
4
Embora eu concorde com a maior parte disso, o fato de o operador de módulo não poder ser usado em tipos de ponto flutuante é inconsistente para tipos de dados primitivos, mas isso provavelmente também não seria uma única instrução em nenhum hardware que eu imagino que seja de alguma maneira predominante agora.
@Sion: Pelo menos no x86, o módulo é uma única instrução. ( DIVfaz divisão e módulo) Você me colocou no ponto de consistência.
Billy ONeal
@ Billy ONeal: Módulo de ponto flutuante em uma única instrução? Eu não tenho me reunido ultimamente para saber por mim mesmo. Se for esse o caso, o operador do módulo deve se tornar aplicável aos tipos de ponto flutuante.
3
@DonalFellows: FORTRAN tinha o operador de exponenciação muito antes de ter algo parecido com o suporte de bignum.
Supercat
1
@DonalFellows: Um operador de energia não é tão útil com números inteiros quanto com flutuadores, mas, para pequenas potências (especialmente quadradas), poderia definitivamente ter seus usos. Pessoalmente, gosto da abordagem de transformar operadores em letras (como Pascal faz com divou FORTRAN com .EQ.); dependendo das regras de espaço em branco do idioma, pode ser possível ter um número arbitrário de operadores sem exigir que sejam palavras reservadas.
23814
41

Esta pergunta é responsável por C ++: Stroustrup, "Design and Evolution of C ++" discute isso na seção 11.6.1, pp. 247-250.

Havia objeções gerais à adição de um novo operador. Seria adicionado à tabela de precedência já complicada demais. Os membros do grupo de trabalho pensaram que isso daria apenas uma pequena comodidade em relação a uma função, e eles queriam substituir suas próprias funções algumas vezes.

Não havia um bom candidato para um operador. ^é ou-exclusivo e ^^confusão convidado por causa da relação entre &e |e &&e ||. !era inadequado, pois haveria a tendência natural de escrever !=para exponenciação de um valor existente, e isso já foi adotado. O melhor disponível pode ter sido *^, do qual aparentemente ninguém realmente gostou.

Stroustrup considerou **novamente, mas já tem um significado em C: a**pé avezes o que quer que paponte e char ** c;declara ccomo um ponteiro para o qual apontar char. Introduzir **como um token que significa "declaração de um ponteiro para o ponteiro para", "vezes o que a próxima coisa aponta para" (se for um ponteiro) ou "exponenciação" (se seguido por um número) causou problemas de precedência. a/b**pteria que analisar como a/(b**p)se p fosse um número, mas (a/b) * *pse p fosse um ponteiro, isso teria que ser resolvido no analisador.

Em outras palavras, isso seria possível, mas complicaria a tabela de precedência e o analisador, e ambos já são muito complicados.

Não conheço a história sobre Java; tudo o que eu poderia fazer seria especular. Quanto ao C, onde começou, todos os operadores C são facilmente traduzidos em código de montagem, em parte para simplificar o compilador e, em parte, para evitar ocultar funcionalidades demoradas em operadores simples (o fato de que operator+()outros poderiam ocultar uma grande complexidade e ocorrência de desempenho foi um) das primeiras queixas sobre C ++).

David Thornley
fonte
2
Boa resposta. Eu acho que o Java tentou simplificar o C a esse respeito, então ninguém queria adicionar um novo operador. É uma pena que ninguém me perguntou, eu certamente teria gostado *^. : D
maaartinus 12/08
1
C foi criado para fazer a formatação de texto. O Fortran foi construído para fazer matemática e possuía matemática complexa, de potência e de matriz 20 anos antes.
Martin Beckett
@ Martin Beckett: Você consegue encontrar evidências de que o C foi criado para formatação de texto? Parece-me uma linguagem muito desajeitada para isso, e o que eu li sobre a origem do C diz que ele foi projetado para a programação do sistema principalmente para o Unix.
David Thornley 29/05
@DavidThornley - Foi projetado para escrever o Unix, mas todos os primeiros usos do Unix parecem ter sido formatação de texto e, por esse tempo, possui uma extensa cadeia de caracteres e uma biblioteca de E / S.
Martin Beckett
6
+1: O significado existente para a**pé o assassino. (Os hacks Para contornar esse problema ... Brr!)
Donal Fellows
13

Eu suspeito que é porque todo operador que você apresenta aumenta a complexidade do idioma. A barreira de entrada é, portanto, muito alta. Eu me vejo usando exponenciação muito, muito raramente - e estou mais do que feliz em usar uma chamada de método para fazer isso.

Jon Skeet
fonte
3
Eu usaria x**2e x**3não tão raramente. E uma implementação mágica do pow que o compilador conhece e otimiza para os casos simples seria legal.
precisa saber é o seguinte
2
@CodeInChaos: No entanto x * xe x * x * xnão são maus substitutos para a praça e cubo.
precisa
5
@ David, você não pode simplesmente escrever x*xse x é uma expressão. Na melhor das hipóteses, o código se torna pesado e, na pior das hipóteses, mais lento ou até errado. Então, você precisa definir suas próprias funções Square e Cube. E mesmo assim, o código seria mais feio do que usar ** como operador de energia.
precisa saber é o seguinte
1
@ David Eu preciso colocar parênteses sim, mas não preciso repetir a expressão várias vezes e inchar o código-fonte. O que reduz muito a legibilidade. E a eliminação de subexpressão comum só é possível se o compilador puder garantir que a expressão esteja livre de efeitos colaterais. E pelo menos o jitter .net não é muito inteligente nesse sentido.
CodesInChaos
11

Os designers da linguagem Java e da biblioteca principal decidiram relegar a maioria das operações matemáticas para a classe Math . Veja Math.pow () .

Por quê? Flexibilidade para priorizar o desempenho em vez de precisão bit a bit. Seria contrário ao resto das especificações de linguagem dizer que o comportamento dos operadores matemáticos internos pode variar de plataforma para plataforma, enquanto a classe Math afirma especificamente que o comportamento potencialmente sacrifica a precisão do desempenho, portanto, cuidado com o comprador:

Diferentemente de alguns dos métodos numéricos da classe StrictMath , todas as implementações das funções equivalentes da classe Math não são definidas para retornar os mesmos resultados bit a bit. Esse relaxamento permite implementações de melhor desempenho, onde a reprodutibilidade estrita não é necessária.


fonte
6

A exponenciação fazia parte do Fortran desde o início, porque visava diretamente a programação científica. Engenheiros e físicos costumam usá-lo em simulações, porque as relações com a lei de potência são comuns na física.

O Python também tem uma forte presença na computação científica (por exemplo, NumPy e SciPy). Isso, junto com seu operador de exponenciação, sugere que ele também visava a programação científica.

C, Java e C # têm raízes na programação do sistema. Talvez essa seja uma influência que manteve a exponenciação fora do grupo de operadores suportados.

Apenas uma teoria.

duffymo
fonte
4

C definiu apenas operadores para operações aritméticas comuns acessíveis com a ALU. Seu principal objetivo era criar uma interface legível para o código Assembly.

O C ++ não alterou o comportamento de qualquer operador porque desejava que toda a base de código escrita em C fosse compatível.

O Java fez o mesmo porque não queria intimidar os programadores C ++ existentes.

Tugrul Ates
fonte
Quando o C foi criado, a multiplicação e a divisão não eram raras em hardware e precisavam ser implementadas em software. No entanto, C possui operadores de multiplicação e divisão.
siride
@siride: Que eu saiba, o PDP-7, o primeiro computador a rodar o Unix, teve multiplicação e divisão de hardware por meio do EAE. Por favor, veja: bitsavers.org/pdf/dec/pdp7/F-75_PDP-7userHbk_Jun65.pdf
Tugrul Ates
1

Bem, porque todo operador que faria sentido para uma energia já está em uso. ^ é XOR e ** define um ponteiro para um ponteiro. Então, em vez disso, eles apenas têm uma função que faz a mesma coisa. (como pow ())

Skyler Saleh
fonte
@RTS - Um desenvolvedor de idiomas está realmente procurando mais senso do que eficiência?
Um bom desenvolvedor de uma linguagem de programação analisa os dois. Não posso dizer nada sobre java. Mas em c ++ a função pow () é calculada em tempo de compilação. E é tão eficiente quanto os operadores regulares.
@RTS: A pow()função executa seu cálculo em tempo de execução, a menos que você tenha um compilador que possa fazer dobragens constantes pow(), o que duvido muito. (Alguns compiladores dar-lhe a opção de usar intrínsecos do processador para realizar o cálculo, no entanto.)
Em silico
@ No silico, eu não quis dizer que ele calcula o valor final, quis dizer que os compiladores otimizarão a chamada de função, para que você tenha apenas a equação bruta.
2
@josefx: Claro que é uma boa razão. Um único *é um token lexical, seja usado para indireção ou multiplicação. Uma **exponenciação de significado seria um ou dois tokens lexicais, e você realmente não deseja que seu lexer tenha que bater na tabela de símbolos para tokenizar.
David Thornley # 03
0

O fato é que operadores aritméticos são apenas atalhos de funções. (Quase) Tudo o que você faz com eles pode ser feito com uma função. Exemplo:

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

É apenas mais detalhado, então não vejo nada de errado em usar funções para executar 'poder de'.

Xeo
fonte
-1

Adição / subtração / negação e multiplicação / divisão são operadores matemáticos básicos. Se você tornasse a energia um operador, onde você para? Operador de raiz quadrada? Operador N-root? Operador de logaritmo?

Não posso falar pelos criadores, mas posso dizer que acho que seria complicado e não ortogonal ter esses operadores no idioma. O número de caracteres não alfanuméricos / espaços em branco restantes no teclado é bastante limitado. Como é, é estranho que exista um operador de módulo em C ++.

Sion Sheevok
fonte
+1 - Não vejo por que ter modum operador é estranho. Geralmente é uma única instrução. É uma operação primitiva em números inteiros. É usado em quase todos os lugares na ciência da computação. (Implementação de coisas como buffers delimitadas sem modiria feder)
Billy ONeal
@ Billy ONeal: Estranho por causa da inconsistência entre poder usar com tipos inteiros e tipos de ponto flutuante. Absolutamente útil e eu nem sonharia em removê-lo. Apenas peculiar é tudo.