Pourquoi il n'y a pas d'opérateur de puissance en Java / C ++?


23

Bien qu'il existe un tel opérateur - **en Python, je me demandais pourquoi Java et C ++ n'en ont pas non plus.

Il est facile d'en créer une pour les classes que vous définissez en C ++ avec surcharge d'opérateur (et je pense qu'une telle chose est également possible en Java), mais lorsque vous parlez de types primitifs tels que int, double et ainsi de suite, vous devrez utiliser la bibliothèque fonctionnent comme Math.power(et doivent généralement lancer les deux pour doubler).

Alors - pourquoi ne pas définir un tel opérateur pour les types primitifs?


8
En C ++, nous ne pouvons pas créer nos propres opérateurs. Vous ne pouvez surcharger que les opérateurs existants.

1
@Mahesh, donc je peux créer ma propre classe Number et surcharger ^ opérateur pour être une puissance. Cela n'a vraiment pas d'importance.

22
@RanZilber: Cela importe parce que la priorité de l' ^opérateur ne correspond pas à la priorité de l'exponentiation. Considérez l'expression a + b ^ c. En mathématiques, l'exponentiation est effectuée en premier ( b ^ c), puis la puissance résultante est ajoutée à a. En C ++, l'addition est effectuée en premier ( a + b) puis l' ^opérateur est effectué avec c. Donc, même si vous avez implémenté l' ^opérateur pour signifier l'exponentiation, la priorité surprendra tout le monde.
In silico

2
@RamZilber - ^est un XOR en C ++. Il est conseillé qu'un opérateur surchargé ne fasse pas la différence avec ce qu'un type de données primitif fait en l'utilisant.

4
@RanZilber: Parce qu'il n'est pas du tout intuitif d'utiliser l'un des opérateurs que vous mentionnez pour signifier l'exponentiation. Je voudrais sérieusement remettre en question la compétence de tout programmeur C ++ qui surcharge l' ++opérateur ou l' !opérateur et. Al. pour signifier l'exponentation. Mais vous ne pouvez pas de toute façon, car les opérateurs dont vous parlez n'acceptent qu'un seul argument; l'exponentiation nécessite deux arguments.
In silico

Réponses:


32

De manière générale, les opérateurs primitifs en C (et par extension C ++) sont conçus pour être implémentables par du matériel simple dans à peu près une seule instruction. Quelque chose comme l'exponentiation nécessite souvent un support logiciel; il n'est donc pas là par défaut.

En outre, il est fourni par la bibliothèque standard de la langue sous la forme de std::pow .

Enfin, cela pour les types de données entiers n'aurait pas beaucoup de sens, car la plupart des petites valeurs d'exponentiation soufflent la plage requise pour int, c'est-à-dire jusqu'à 65 535. Bien sûr, vous pouvez le faire pour les doubles et les flottants mais pas pour les entiers, mais pourquoi rendre le langage incohérent pour une fonctionnalité rarement utilisée?


4
Bien que je sois d'accord avec la plupart de ces éléments, le fait que l'opérateur de module ne puisse pas être utilisé sur les types à virgule flottante est incohérent pour les types de données primitifs, mais cela ne serait probablement pas non plus une instruction unique sur un matériel quelconque, j'imagine, est de toute façon répandue maintenant.

@Sion: Au moins sur x86, le module est une instruction unique. ( DIVfait à la fois la division et le module).
Billy ONeal

@Billy ONeal: module à virgule flottante en une seule instruction? Je n'ai pas fouillé dans l'assemblage ces derniers temps pour savoir par moi-même. Si tel est le cas, alors l'opérateur de module devrait être rendu applicable aux types à virgule flottante.

3
@DonalFellows: FORTRAN disposait de l'opérateur d'exponentiation bien avant d'avoir quelque chose qui ressemble à un support bignum.
supercat

1
@DonalFellows: Un opérateur de puissance n'est pas aussi utile avec des entiers qu'avec des flottants, mais pour les petites puissances (en particulier la quadrature), il pourrait certainement avoir ses utilisations. Personnellement, j'aime l'approche consistant à faire des opérateurs à partir de lettres (comme Pascal le fait avec divou FORTRAN avec .EQ.); en fonction des règles d'espace du langage, il peut être possible d'avoir un nombre arbitraire d'opérateurs sans exiger qu'ils soient des mots réservés.
supercat

41

Cette question peut être résolue pour C ++: Stroustrup, "Design and Evolution of C ++" en discute dans la section 11.6.1, pp. 247-250.

Il y avait des objections générales à l'ajout d'un nouvel opérateur. Cela ajouterait au tableau de priorité déjà trop compliqué. Les membres du groupe de travail pensaient que cela n'apporterait qu'une commodité mineure à la fonction et ils voulaient parfois pouvoir remplacer leurs propres fonctions.

Il n'y avait pas de bon candidat pour un opérateur. ^est exclusif-ou, et a ^^invité la confusion en raison de la relation entre &et |et &&et ||. !était inadapté car il y aurait une tendance naturelle à écrire !=pour l'exponentiation d'une valeur existante, et cela a déjà été pris. Le meilleur disponible peut avoir été*^ , ce que personne n'a vraiment aimé.

Stroustrup a réfléchi à **nouveau, mais il a déjà une signification en C: a**pest afois ce qui ppointe vers, et char ** c;déclare ccomme un pointeur vers un pointeur vers char. L'introduction en **tant que token signifiant "déclaration d'un pointeur vers un pointeur vers", "multiplie par ce que la prochaine chose pointe" (s'il s'agit d'un pointeur) ou "exponentiation" (si elle est suivie d'un nombre) a causé des problèmes de priorité. a/b**pdevrait analyser comme a/(b**p)si p était un nombre, mais (a/b) * *psi p était un pointeur, cela devrait donc être résolu dans l'analyseur.

En d'autres termes, cela aurait été possible, mais cela aurait compliqué la table de priorité et l'analyseur, et les deux sont déjà trop compliqués.

Je ne connais pas l'histoire de Java; je ne pouvais que spéculer. En ce qui concerne C, où il a commencé, tous les opérateurs C sont facilement traduits en code assembleur, en partie pour simplifier le compilateur et en partie pour éviter de cacher des fonctionnalités chronophages dans les opérateurs simples (le fait que operator+()et d'autres pourraient masquer une grande complexité et des résultats de performance en était un). des premières plaintes concernant C ++).


2
Bonne réponse. Je suppose que Java a essayé d'être simplifié C à cet égard, donc personne ne voulait ajouter un nouvel opérateur. Dommage que personne ne me l'ait demandé, j'aurais sûrement aimé *^. : D
maaartinus

1
C a été conçu pour effectuer la mise en forme du texte. Fortran a été construit pour faire des mathématiques et avait des mathématiques complexes, de puissance et de matrice 20 ans plus tôt.
Martin Beckett

@Martin Beckett: Pouvez-vous trouver des preuves que C a été construit pour la mise en forme du texte? Cela me semble un langage très maladroit pour cela, et ce que j'ai lu sur l'origine de C dit qu'il a été conçu principalement pour la programmation système pour Unix.
David Thornley

@DavidThornley - Il a été conçu pour écrire Unix, mais toutes les premières utilisations d'Unix semblent avoir été du formatage de texte, et pour le moment, il possède une vaste chaîne et une bibliothèque d'E / S.
Martin Beckett

6
+1: Le sens actuel de a**pest le tueur. (Les astuces pour contourner ce problème… Brr!)
Donal Fellows

13

Je suppose que c'est parce que chaque opérateur que vous introduisez augmente la complexité de la langue. La barrière à l'entrée est donc très élevée. Je me retrouve à utiliser l'exponentiation très, très rarement - et je suis plus qu'heureux d'utiliser un appel de méthode pour le faire.



3
J'utiliserais x**2et x**3pas si rarement. Et une implémentation de Magic Pow que le compilateur connaît et optimise pour les cas simples serait bien.
CodesInChaos

2
@CodeInChaos: Cependant x * xet x * x * xne sont pas de mauvais substituts pour le carré et le cube.
David Thornley

5
@David, vous ne pouvez pas simplement écrire x*xsi x est une expression. Dans le meilleur des cas, le code devient lourd et dans le pire, plus lent ou même erroné. Vous devez donc définir vos propres fonctions Square et Cube. Et même alors, le code serait plus laid que d'utiliser ** comme opérateur de puissance.
CodesInChaos

1
@David J'ai besoin de mettre des parenthèses oui, mais je n'ai pas besoin de répéter l'expression plusieurs fois et de gonfler le code source. Ce qui réduit beaucoup la lisibilité. Et l'élimination de la sous-expression commune n'est possible que si le compilateur peut garantir que l'expression est exempte d'effets secondaires. Et au moins, la gigue .net n'est pas trop intelligente à cet égard.
CodesInChaos

11

Les concepteurs du langage Java et de la bibliothèque principale ont décidé de reléguer la plupart des opérations mathématiques à la classe Math . Voir Math.pow () .

Pourquoi? Flexibilité pour hiérarchiser les performances sur la précision bit à bit. Il irait à l'encontre du reste des spécifications de langage de dire que le comportement des opérateurs mathématiques intégrés pourrait varier d'une plateforme à l'autre, tandis que la classe Math déclare spécifiquement que le comportement sacrifie potentiellement la précision pour les performances, donc l'acheteur se méfie:

Contrairement à certaines des méthodes numériques de la classe StrictMath , toutes les implémentations des fonctions équivalentes de la classe Math ne sont pas définies pour renvoyer les mêmes résultats bit à bit. Cette relaxation permet des implémentations plus performantes où une reproductibilité stricte n'est pas requise.


6

L'exponentiation faisait partie de Fortran depuis le début car elle visait carrément la programmation scientifique. Les ingénieurs et les physiciens l'utilisent souvent dans les simulations, car les relations entre les lois de puissance sont courantes en physique.

Python est également très présent dans le calcul scientifique (par exemple NumPy et SciPy). Cela, avec son opérateur d'exponentiation, suggère qu'il visait également la programmation scientifique.

C, Java et C # ont des racines dans la programmation système. C'est peut-être une influence qui a exclu l'exponentiation du groupe des opérateurs pris en charge.

Juste une théorie.


4

C uniquement des opérateurs définis pour les opérations arithmétiques courantes accessibles avec l'ALU. Son objectif principal était de créer une interface lisible par l'homme pour le code d'assemblage.

C ++ n'a changé aucun comportement d'opérateur car, il voulait que toute la base de code écrite en C soit conforme.

Java a fait de même car il ne voulait pas intimider les programmeurs C ++ existants.


Lorsque C a été créé, la multiplication et la division ne manquaient pas souvent de matériel et devaient être implémentées dans le logiciel. Pourtant, C a des opérateurs de multiplication et de division.
siride

@siride: À ma connaissance, le PDP-7, le premier ordinateur à exécuter Unix, avait une multiplication et une division matérielles via son EAE. Veuillez consulter: bitsavers.org/pdf/dec/pdp7/F-75_PDP-7userHbk_Jun65.pdf
Tugrul Ates

1

Eh bien parce que chaque opérateur qui aurait du sens pour une puissance est déjà utilisé. ^ est XOR et ** définit un pointeur vers un pointeur. Au lieu de cela, ils ont juste une fonction qui fait la même chose. (comme pow ())


@RTS - Un développeur langauge recherche-t-il vraiment plus de sens que d'efficacité?

Un bon développeur d'un langage de programmation regarde les deux. Je ne peux rien dire sur java. Mais en c ++, la fonction pow () est calculée au moment de la compilation. Et est tout aussi efficace que les opérateurs réguliers.

@RTS: La pow()fonction effectue son calcul lors de l'exécution, à moins que vous n'ayez un compilateur capable de faire un pliage constant pow(), ce dont je doute fortement. (Certains compilateurs vous donnent cependant la possibilité d'utiliser les intrinsèques du processeur pour effectuer le calcul.)
In silico

@In silico, je ne voulais pas dire qu'il calcule la valeur finale, je voulais dire que les compilateurs optimiseraient l'appel de fonction, vous n'avez donc que l'équation brute.

2
@josefx: Bien sûr, c'est une bonne raison. Un simple *est un jeton lexical, qu'il soit utilisé pour l'indirection ou la multiplication. Une **exponentiation signifiant serait un ou deux jetons lexicaux, et vous ne voulez vraiment pas que votre lexer doive frapper la table des symboles pour effectuer une tokenisation.
David Thornley

0

Le fait est que les opérateurs arithmétiques ne sont que des raccourcis de fonctions. (Presque) Tout ce que vous faites avec eux peut être fait avec une fonction. Exemple:

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

C'est juste plus verbeux, donc je ne vois rien de mal à utiliser des fonctions pour effectuer la «puissance de».


-1

L'addition / soustraction / négation et la multiplication / division sont des opérateurs mathématiques de base. Si vous deviez faire de l'électricité un opérateur, où vous arrêteriez-vous? Opérateur racine carrée? Opérateur N-root? Opérateur de logarithme?

Je ne peux pas parler pour leurs créateurs, mais je peux dire que je pense que ce serait devenu lourd et non orthogonal d'avoir de tels opérateurs dans la langue. Le nombre de caractères non alphanumériques / espaces blancs restant sur le clavier est plutôt limité. En l'état, il est étrange qu'il y ait un opérateur de module en C ++.


+1 - Je ne vois pas pourquoi avoir modcomme opérateur est étrange. Il s'agit généralement d'une seule instruction. C'est une opération primitive sur des entiers. Il est utilisé presque partout en informatique. (L'implémentation de choses comme des tampons bornés sans modpuerait)
Billy ONeal

@Billy ONeal: étrange en raison de l'incohérence entre pouvoir utiliser avec des types entiers et des types à virgule flottante. Absolument utile et je ne rêverais pas de l'enlever. Tout simplement excentrique est tout.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.