11
votes

Y a-t-il une raison quelconque certaines langues permettent un module négatif?

Je suis curieux de ces langues (Java, C ...) qui ignorent la définition mathématique de l'opération de module.

Quel est le point de retourner des valeurs négatives dans une opération de module (que, par définition, devrait toujours renvoyer un nombre positif)?


15 commentaires

Il n'y a pas de définition qui dit que tout devrait être "toujours positif". L'arithmétique modulaire concerne les cours d'équivalence et tout représentant est aussi bon que tout autre.


@OLICHARLESWORTHORD: C99 Spécifie-t-elle des contraintes sur les arithmétiques modulo?


Ce serait Nice d'avoir une opération de module qui produisait des représentants canoniques.


@Matt: Oui, implicitement. Il charge que (a / b) * b + a% b == a et cette division tronque vers zéro.


@KerReksb Le problème ici est que les nombres négatifs tombent dans différentes classes d'équivalence que les positifs, contrairement au module mathématique.


Il est ridicule que quelque chose d'aussi simple que cela n'a pas de comportement défini dans un tel langage de programmation majeur. Vous ne pouvez pas utiliser ABS (% B) car certaines implémentations mirent à 0, certaines ne le font pas. Et vous finissez par tirer des trucs comme ((a% b) + b)% B et cette sensation étrange, quand elle va se casser.


@Pez: Ce n'est pas ridicule. C a été écrit à l'origine pour refléter la réalité des plates-formes qu'elle a été exécutée. Je ne suis pas sûr de comprendre vos exemples ...


@Oli, -7% 3 peut être 2 ou -2 ou même -1, selon le compilateur. Quelle utilisation est ce résultat si je ne peux pas être sûr que ce sera?


@PEZCODE: Il n'y a que deux réponses possibles. Pour répondre à votre question: vous ne devriez pas écrire des programmes à la pré-C99 qui s'appuient, tout comme vous ne devriez pas écrire des programmes dépendant du comportement spécifique de / .


@soulChecket: Je pense que cette déclaration doit être rendue plus précise: le reste de la division n par 3 est un représentant de la classe de résidus de N et le reste de la division n par -3 est un représentant de la classe de - n . Cependant, tout représentant est toujours aussi bon que tout autre en termes d'arithmétique modulaire. (Par conséquent, il vaut mieux penser à "reste" que de "modulo", dans le sens de (A / B) * B + (a% b) == A .)


Qu'est-ce que (GMT) heure de la journée était (Time_T) (- 10000)? Si vous avez répondu 21:13:20 au lieu de -2: -46: -40, alors vous aimerez probablement la définition de Python de % mieux que C99.


@ DAN04: Peut-être. Mais je parie qu'il est possible de trouver un exemple différent lorsque la définition de C99 est la plus utile.


@KerReksb Bien dans mes cours de mathématiques "Modulus" a également été utilisé pour la magnitude d'un nombre complexe (qui est non négatif) et je n'ai pas encore vu quiconque dans la théorie de congruence qui suppose que le module peut être négatif. Il y a donc des précédents là-bas;) personnellement, je pense que c'est généralement la solution la plus intuitive d'avoir le module non négatif, mais je conviens qu'il n'y a pas d'accord universel à ce sujet. Heck Même Knuth (qui a certainement un fond mathématique) a défini le mod de manière à ce que cela puisse être négatif.


@Voo: Allez, vous savez mieux que de traverser le modulus autre (c'est-à-dire une valeur absolue) ici pour regarder la mouche des éclats d'obus. Personne n'a été confus à ce sujet! :-)


@Kerrek oh mais je me suis senti si intelligent de ce petit Nitpick;) Je veux dire à la fin, je pense toujours qu'il est moins intuitif d'avoir un module négatif, mais cela ne m'a pas causé de nuits instantanées - une dernière chose que vous devez vous souvenir .


6 Réponses :


1
votes

Une raison pragmatique de retourner une valeur négative pour le module serait que la mise en œuvre du module de mise en œuvre du matériel.

Les normes laissent donc mal définies, de sorte que les compilateurs puissent faire tout ce qui leur est plus simple.


3 commentaires

Il est bien défini dans certaines normes; Java et C99, par exemple.


Êtes-vous sûr que C99 définit cela pour des nombres négatifs?


Oui. Il est défini en termes de l'opérateur / , dont le comportement est maintenant bien défini pour les valeurs négatives.



8
votes

en Java au moins, ce n'est pas un opérateur de modulus - c'est un Opérateur de reste .

Je crois que la raison de celle-ci est choisie de cette façon de faire fonctionner cette relation (de la JLS):

L'opération de reste des opérandes qui sont des entiers après la promotion numérique binaire (§ 5.6.2) produisent une valeur de résultat telle que (A / B) * B + (A% B) est égal à a. Cette identité tient même dans le cas particulier que le dividende est l'entier négatif de la plus grande magnitude possible pour son type et le diviseur est -1 (le reste est de 0). Il résulte de cette règle que le résultat de l'opération de restante ne peut être négatif que si le dividende est négatif et ne peut être positif que si le dividende est positif; De plus, la magnitude du résultat est toujours inférieure à la magnitude du diviseur.

Cette relation d'égalité semble être une chose raisonnable à utiliser dans le cadre de la définition. Si vous prenez la division tronquante vers zéro pour être donnée, cela vous laisse avec un reste négatif.


11 commentaires

@Jerrycoffin: Oui, mais en conjonction avec la division tronquée à 0, vous vous retrouvez avec cela. J'ajouterai le peu de division à ma réponse.


La même chose est largement vraie en C99: l'opérateur n'a aucun nom autre que % , mais son résultat est "le reste" ; et la même relation euclidienne entre A et b est true (sauf que c ne possède pas le cas particulier pour int_min% -1 - c'est-à-dire comportement non défini).


@CAF: Où trouvez-vous quelque chose dire dire int_min% -1 donne UB? Je ne vois pas que nulle part dans les normes C ou C ++.


@Jerrycoffin: En fait, je pense que vous avez raison - int_min / -1 peut être indéfini en raison d'un débordement entier, mais il semble que le reste soit toujours zéro. Cependant, je peux obtenir gcc à sigfpe avec cette opération ...


@JerryCoffin: Il semble que R. Et Jens a été ici avant nous - Le projet de norme actuel l'appelle maintenant explicitement comme un comportement indéfini.


La raison que vous avez donnée n'a de sens que parce que la division est également définie dans le mauvais sens. La division devrait toujours "arrondir" plutôt que "rond vers zéro".


@CAF: Oui, mais cela dit simplement que la valeur est indéfinie. Le comportement non défini est une histoire différente totale .


@JerryCoffin: Je ne suis pas sûr du commentaire que vous parlez de - int_min / -1 est vraiment un comportement non défini en raison du débordement entier signé, tout comme int_max + 1 est . La nouvelle langue dans le projet faisant référence à l'opérateur % dit spécifiquement que le comportement est indéfini.


@CAF: Je faisais référence au dernier commentaire précédent, mais int_min / -1 n'est pas nécessairement indéfini. Cela donnera à UB dans le complément de 2, mais pas de signe / de magnitude ou de 1 complément (pour les deux, int_min / -1 == int_max). La seule situation dans laquelle je peux voir C99 ou N3290 dire% donne à UB est si le deuxième opérande est 0 (C99: §6.5.5 / 5, N3290: §5.6 / 4).


@JerryCoffin: Oui, je veux dire potentiel ub là-bas, il est donc plus proche de -int_max - 1 à cet égard. Wrt to % c'est N1548 qui inclut la langue effectuant explicitement le comportement des deux A / B et a% b non défini si a / b n'est pas représentable.


@CAF: Ah, je n'avais pas envisagé le nouveau projet C - Je suis toujours trop occupé à essayer de restarner C ++ pour passer beaucoup de temps dessus.



6
votes

de Wikipedia (mon emphase):

donné deux nombres positifs, un (le dividende) et n (le diviseur), un modulo n (abrégé comme un mod n) peut être considéré comme le reste, sur la division d'un par n. Par exemple, l'expression "5 mod 4" serait évaluer à 1 parce que 5 divisé par 4 laisse un reste de 1, tandis que "9 mod 3 "évaluerait à 0 parce que la division de 9 par 3 laisse un reste de 0; Il n'y a rien à soustraire de 9 après la multiplication 3 fois 3. (Notez que faire la division avec une calculatrice ne sera pas Montrez-vous le résultat mentionné ici par cette opération, le quotient sera exprimé comme une décimale.) lorsque l'un ou l'autre est négatif, Cette définition naïve se décompose et les langages de programmation diffèrent Comment ces valeurs sont définies. bien que typiquement effectué avec un et n à la fois des entiers, de nombreux systèmes informatiques permettent d'autres types de opérandes numériques. La gamme de nombres pour un module entier de n est 0 à n - 1. (n mod 1 est toujours 0; n mod 0 est indéfini, éventuellement entraînant une erreur "division par zéro" d'erreur dans la programmation informatique Langues) Voir l'arithmétique modulaire pour une convention plus ancienne et associée appliqué en tant que théorie des nombres.


4 commentaires

Mathématiquement, le reste entier est toujours compris entre 0 et un de moins que le diviseur, en supposant un diviseur positif. Ce conflit avec les langages de programmation de définition laids (et les processeurs) ont tendance à utiliser. L'ancienne définition est extrêmement utile, par ex. pour les calculs de date. Ce dernier est inutile, sauf lors de l'utilisation d'entiers comme approximations de valeurs non intégrées (par exemple point fixe).


@R ..: Je ne dirais pas "mathématiquement" (à moins que vous n'ayez une théorie authentique pour remonter ainsi), mais plutôt "classiquement". (Pour des dividendes positifs, cela n'est pertinent que pour les diviseurs négatifs bien sûr.)


Eh bien, la définition algébrique de la division n / d j'ai toujours appris était n = qd + r 0≤r . :-)


@R ..: En fait, c'est un bon point - c'est toujours juste une convention, mais c'est la façon dont le théorème de la division est indiqué. (Bien que notez que le théorème est également valable pour tout autre choix fixe de COSET.) Cela dit, il s'agit d'un bel argument en faveur de la fixation du reste à être positif.



3
votes

La plupart d'entre eux ne sont pas définis pour renvoyer un module. Ce qu'ils sont définis à revenir, c'est un reste, pour lequel des valeurs positives et négatives sont également raisonnables.

en C ou C ++, il est assez raisonnable de dire qu'il devrait produire tout ce que le matériel sous-jacent produit. Cette excuse / la raison ne fonctionne pas aussi bien pour Java cependant.

Notez également que, dans C89 / 90 et C ++ 98/03, le reste pourrait être soit positif, soit négatif, à condition que les résultats du reste et de la division fonctionnent ensemble ( (A / B) * B + a% b == A ). En C99 et C ++ 11, les règles ont été resserrées afin que la division doit tronquer à zéro et s'il y a un reste, il doit être négatif.


0 commentaires

1
votes

en aucun des normes C ou Java n'est % appelé opérateur de modulus - plutôt, il renvoie le le reste . .

Il est défini pour renvoyer des nombres négatifs pour les dividendes négatifs afin que la relation (A / B) * B + A% B == a est contenant, tant que A / B < / code> est représentable. Étant donné que l'opérateur de division est défini pour tronquer à zéro, cela contraint le signe du reste.


0 commentaires

6
votes

Je doute que l'opérateur reste conçu délibérément pour avoir ces sémantiques, que je suis d'accord ne sont pas très utiles. (Souhaitez-vous écrire un programme de calendrier qui montre le dimanche des jours de la semaine, anti-samedi, anti-vendredi, ..., anti-lundi des dates avant l'époque?)

Plutôt, les restes négatifs sont un effet secondaire du chemin La division entière est définie. xxx

si a div b est défini comme trunc (A / B) , vous obtenez C % . Si A DIV B est défini comme Plancher (A / B) , vous obtenez l'opérateur % de Python. D'autres définitions sont possibles.

Donc, la vraie question est la suivante:

Pourquoi C ++, Java, C #, etc. Utilisez une division entière tronquante?

parce que c'est La façon dont c.

pourquoi c utilise la division tronquante?

à l'origine, c n'a pas spécifié comment / doit gérer des nombres négatifs. Il l'a laissé jusqu'au matériel.

dans la pratique, chaque implémentation C significative C a utilisé une division tronquante, donc en 1999, ces sémantiques ont formellement fait une partie de la norme C.

Pourquoi Utilisation matérielle de tronquage de la division?

Parce qu'il est plus facile (= moins cher) de mettre en œuvre en termes de division non signée. Vous calculez simplement ABS (A) div ABS (B) et retournez le signe si (A <0) xor (B <0) . . La division revêtue a l'étape supplémentaire de soustraire 1 du quotient si le reste est non nul.


1 commentaires

Je me demande pour quelles applications la version C99 de la division signée est réellement utile? Dans mon expérience, dans des cas qui nécessitent une exactitude, je dois presque toujours gérer des nombres négatifs spécialement, de sorte que les divisions impliquant des nombres négatifs renvoient des valeurs non spécifiées ne seraient pas beaucoup moins utiles que les disposer de la norme. Je pouvais voir un argument pour laisser le comportement non spécifié (qui pourrait accélérer les choses sur le matériel qui manque d'instruction de division signée), mais pas beaucoup d'avantage à la norme comme écrit.