Existe-t-il un langage de programmation où 1/6 se comporte comme 1.0 / 6.0?

11

Pendant que je programmais en C ++ il y a quelques jours, j'ai fait cette erreur (que j'ai l'habitude de faire!). Dans une partie de mon code, j'avais 1/6 et je m'attendais à ce que ce soit 0.16666666666 ce qui n'est pas le cas. Comme vous le savez tous, le résultat est 0 - C, C ++, Java, Python, tous se comportent de la même manière.

Je le poste sur ma page Facebook et maintenant il y a débat pour savoir s'il existe un langage de programmation qui 1/6se comporte de la même manière que 1.0/6.0.

Pouya
la source
5
Haskell. 1/6 = 0,1666666666666666666
t123
PowerShell génère 0,166666666666667, ce qui m'a surpris car 1 est un entier. Je parie qu'il y a d'autres langages .NET qui génèrent la valeur que vous attendiez.
JohnL
Il y en a, en théorie, un nombre illimité. Voici un autre: Rebol , ainsi que des dérivés comme Orca, Red, etc. >> 1 / 6->== 0.166666666666667
Izkata
Lua fait ça. Il n'a qu'un seul type de numéro , qui est généralement le même que le double de C.
Machado
In Clojure 1/6est en fait 1/6 (type fractionnaire) qui, contraint Double, est de 1,66666 ...
kaoD

Réponses:

17

Tout le monde a oublié Pascal?

1/6rendements 0.1666666...(quelle que soit la précision prise en charge).

1 div 6 les rendements 0

On peut se demander si la règle C est une erreur. Presque tous les opérateurs arithmétiques de C, où les opérandes sont du même type, donnent un résultat du même type. Il y a quelque chose à dire pour la cohérence.

De plus, puisque C est principalement destiné au code au niveau du système, la plupart des programmes C n'utilisent pas du tout de virgule flottante. À un moment donné, l'ajout accidentel de code à virgule flottante à un programme qui n'en aurait pas besoin autrement pourrait être un problème grave. C'est probablement toujours le cas, pour les petits systèmes embarqués - qui, encore une fois, sont une cible majeure pour C.

Dans la plupart des programmes C, tronquer la division entière est probablement exactement ce que vous voulez de toute façon.

Si a 1 / 6donné un résultat à virgule flottante en C, alors:

  • Ce serait une incohérence dans la langue.
  • La norme devrait faire un choix arbitraire dont le type à virgule flottante à utiliser pour le résultat ( doublepeut sembler le choix naturel, mais vous pouvez préférer la précision supplémentaire long double)
  • Le langage devrait encore avoir une opération de division entière; effectuer une addition en virgule flottante puis tronquer ne serait probablement pas suffisant.

C aurait pu fournir des opérateurs distincts pour les deux types de division, mais le deuxième point ci-dessus s'appliquerait toujours: lequel des trois types à virgule flottante serait utilisé pour le résultat? Et comme il est assez facile d'obtenir une division en virgule flottante si vous en avez besoin (utilisez une constante à virgule flottante pour l'un des opérandes ou les deux, ou convertissez l'un des opérandes ou les deux en type à virgule flottante), cela n'a apparemment pas été '' t considéré que important.

Dans la version 1974 du manuel C (soit 4 ans avant la publication de la première édition de K&R), Ritchie ne mentionne même pas la confusion possible:

Le binaire / opérateur indique la division. Les mêmes considérations de type que pour la multiplication s'appliquent

qui dit que si les deux opérandes sont de type intou char, le résultat est de type int.

Oui, c'est une source de confusion pour certains programmeurs C, en particulier les débutants - mais C n'est pas réputé pour être très convivial pour les novices.

Keith Thompson
la source
5
Pascal. Obtenir les détails juste avant que C ne se trompe. ™
Mason Wheeler
Et puis Algol a été une amélioration par rapport à ses successeurs (dans le nombre desquels se trouvent C et Pascal).
Programmeur
Pascal - obtenir les bons détails (donner ou prendre un facteur 10)
Martin Beckett
1
J'ai écrit à l'origine 1.666666..., ce qui est clairement faux. Mon excuse boiteuse est que le programme de test Pascal que j'ai écrit a été imprimé1.6666666666666667E-0001
Keith Thompson
16

En fait, ce comportement a été modifié dans Python 3 et il se comporte maintenant comme prévu ( //est maintenant utilisé pour la division entière).

sepp2k
la source
Merci. Avez-vous en tête d'autres langages de programmation? Famille basique peut-être?
Pouya
1
@Pouya: Ce comportement est standard pour la famille Pascal. /produit toujours une valeur à virgule flottante et un opérateur séparé ( div) est utilisé pour la division entière.
Mason Wheeler
13

Hors des langues dominantes, JavaScript. 1,0 / 6,0 = 1/6 = 0,16666666666666666.

Je ne vois pas cela comme surprenant. En règle générale, si une langue fait la distinction entre les types numériques entiers et à virgule flottante, la division de deux entiers produira un entier tronqué au lieu de flottant. Si ce n'est pas le cas, il s'agira très probablement par défaut d'opérations en virgule flottante. Cela devrait être le comportement attendu de la part du programmeur.

Gardez juste à l'esprit qu'il y a des choses supplémentaires qui pourraient également être en jeu ici, comme l'opérateur de division d'entier séparé déjà mentionné ou la conversion de type implicite.

scrwtp
la source
6
Ce n'est pas "une règle d'or". C'est une règle de C, et une poignée de langages qui ont copié aveuglément les erreurs de C.
Mason Wheeler
4
@MasonWheeler: FORTRAN a-t-il "copié aveuglément l'erreur de C"? Personnellement, je crois que les langages bien conçus devraient utiliser des opérateurs séparés pour la division tronquée par rapport à la division approximative des quantités entières (et aussi, btw, pour la valeur et l'égalité référentielle), mais la décision de conception à l'époque de FORTRAN était probablement raisonnable. Cela ne signifie pas que chaque langue devrait faire les choses comme FORTRAN dans les années 50.
supercat
3
Les langues de niveau inférieur doivent faire ces distinctions. Les langues de niveau supérieur / dynamiques sont souvent mieux sans elles. C'est un compromis de conception. J'apprécie de n'avoir qu'un seul constructeur / type de nombre stupide dans JS, mais j'imagine que je sentirais que JS manquait en essayant d'écrire un moteur 3D haute performance sans contrôle de type plus strict. JS peut avoir un utilitaire qui se chevauche avec d'autres langues, mais je ne pense pas que personne ne le considérera jamais comme un goto pour écrire des choses de type hautes performances plus proches du chrome.
Erik Reppen
2
Les mathématiques en dehors de la programmation prennent en compte des règles de division uniquement entières.
Erik Reppen
5
@MasonWheeler: La programmation n'est pas purement mathématique. Mathématiquement, 1/6 est un nombre rationnel et ne peut pas être représenté exactement par un nombre à virgule flottante binaire. La seule représentation précise est un rapport avec un dénominateur six fois le numérateur.
kevin cline
7

Il existe de nombreuses langues où les ((1/6)*6)résultats en 1, pas en 0. Par exemple, PL / SQL, de nombreux dialectes BASIC, Lua.

Par accident, dans tous ces langages, 1/6 se traduit par 0,166666667 ou 0,16666666666667 ou quelque chose de similaire. J'ai choisi la variante ((1/6) * 6) == 1 pour me débarrasser de ces petites différences.

user281377
la source
7
Ce n'est pas la question.
Roc Martí
20
Par accident, dans tous ces langages, 1/6 se traduit par 0,166666667 ou 0,16666666666667 ou quelque chose de similaire. J'ai choisi la ((1/6)*6)==1variante pour me débarrasser de ces petites différences, mais il semble que j'ai surestimé les compétences en mathématiques de certaines personnes.
user281377
1
@ RocMartí oui, c'est vraiment ...
MattDavey
1
Je serais surpris de voir (1.0 / 6.0) * 6 être exactement égal à 1! L'arrondi du résultat de (1.0 / 6.0) entraînera une petite différence. (Bien qu'il y ait quelques langues par défaut à une précision infinie)
Sjoerd
1
@Sjoerd: Il n'est pas trop surprenant que ce soit exact, en fait. Considérons en décimal le scénario de 1/11 * 11 avec toutes les valeurs précises à cinq chiffres significatifs. La valeur de 1/11 est 9,0909 * 10 ^ -2. Multipliez par 11 et on obtiendrait 99,9999 * 10 / -2 avant d'arrondir. Arrondissez à cinq chiffres significatifs et le résultat sera 1,0000 * 10 ^ 0. Notez que la clé est que la mantisse de 1/6 est "... 0101010101 ...". Si le dernier bit d'une représentation est un "1", la multiplier par six et l'arrondi donnera 1. Si le dernier bit était nul, ce ne serait pas le cas.
supercat
3

Haskell considère que 1/6 et 1,0 / 6,0 sont identiques à 0,16666666666666666. Il rend également 1 / 6.0 et 1.0 / 6 comme étant également la même valeur.

Cela est dû au fait que les types numériques de base dans Haskell ne sont pas tout à fait les mêmes que dans d'autres langues. La vraie division entière est quelque peu ... compliquée.

Ingénieur du monde
la source
2

Oui, Perl le fait. Le one-liner

perl -e '$x=1/6;print "$x\n";'

se traduit par la sortie de:

0.166666666666667

Je pense que PHP fonctionne de la même manière.

Modifié pour ajouter: Je pense également qu'une condition nécessaire (mais pas suffisante) pour 1/6 == 1.0/6.0que la langue en question soit faiblement typée.


la source
Pourquoi diable un typage faible (quoi que ce soit censé signifier) ​​serait-il nécessaire? Il suffit de définir / to (aussi) signifie float division dans le cas où les deux arguments sont des entiers.
1
@delnan - en.wikipedia.org/wiki/Weak_typing Je suppose qu'il pourrait être possible d'avoir un langage fortement typé qui /est surchargé automatiquement en fonction des types des arguments, mais cela semble être une violation du principe de moindre Étonnement à moi ...
2
Un typage faible / fort est mal défini (comme le wiki l'implique également), veuillez l'éviter et soyez précis. Je suppose que votre définition interdit la conversion implicite, mais pas le polymorphisme ad hoc? Si c'est le cas, considérez Haskell, qui n'a pas de conversions implicites mais plutôt bien exécuté (comme dans, fonctionne 99% du temps et peut être compris par les mortels) polymorphisme numérique. Et pourquoi cela serait-il étonnant? Il serait beaucoup plus étonnant (pour ne pas dire ennuyeux) si je devais ajouter un certain nombre de points à chaque instance de n'importe quel opérateur, selon la précision exacte que je souhaite.
2
@JackManey Je pense que pour la plupart des nouveaux arrivants, il est beaucoup plus surprenant que 1/2 soit égal à 0 que si la division de deux entiers entraîne un double. Après que tous les nombres entiers ne soient pas fermés sous division en mathématiques non plus. Comme le souligne delnan, Haskell est un exemple de langage fortement typé dans lequel / sur deux entiers, ne produit pas un entier. Et Python 3 en est un autre.
sepp2k
1
Le langage Haxe est fortement (quoique inféré) typé, et pourtant il n'a pas de division entière, seulement flottant. Alors voilà.
K.Steff
2

Dans Squeak Smalltalk /sur les entiers crée des objets Fraction. Donc, bien que ce ne soit pas la même chose que la division flottante, (1/6)*6renvoie toujours 1.

Céphalopode
la source
Dans tous les dérivés Smalltalk-80 (c'est-à-dire presque tous les Smalltalks). L'ambre est l'une des exceptions contemporaines (ce qui est compréhensible, étant compilé en JavaScript).
herby
2

Oui, je viens de vérifier ma TI-99 / 4A est construit en TI BASIC . Comme elle traite toutes les expressions numériques en virgule flottante, l'opération de division est également en virgule flottante.

 TI BASIC READY
>PRINT 1/6
  .1666666667

>
Jesse C. Slicer
la source
2

MATLAB. Les littéraux numériques sont doubles par défaut.

>> 1/6
ans =
    0.1667
Dima
la source
2

Clojure utilise des fractions par défaut. Ce n'est pas la même chose que 1.0 / 6.0, mais vous pouvez le convertir avec floatou doublequand vous en avez besoin.

user=> (/ 1 6)
1/6
user=> (* (/ 1 6) 2)
1/3
user=> (pos? (/ 1 6)) ; Is 1/6 > 0?
true
user=> (float (/ 1 6))
0.16666667
defhlt
la source
1

Étonnamment, il semble fonctionner correctement dans Windows PowerShell (version 3) .

PS C:\> 1.0 / 6.0
0.166666666666667

PS C:\> 1/6
0.166666666666667

Semble également fonctionner en Python 3 comme l'a mentionné sepp2k. Les deux autres langues que j'ai facilement disponibles sur REPL, Scala et Ruby, font toutes deux une division entière et donnent 0.

KChaloux
la source
0

Le langage Rexx produit toujours une réponse arithmétiquement correcte. Par exemple: 5/2 = 2,5. Rexx est un excellent langage qui n'a pas été suffisamment utilisé. En théorie, quand un compilateur ne peut pas déterminer ce que vous voulez, il vaut mieux faire le bon calcul, cependant, cela peut ne pas être efficace. Rexx fournit également l'opérateur //.

Aucune chance
la source