¿Existe un lenguaje de programación donde 1/6 se comporta igual que 1.0 / 6.0?

11

Mientras programaba en C ++ hace unos días, cometí este error (¡tengo antecedentes de hacerlo!). En una parte de mi código, tenía 1/6 y esperaba que fuera 0.16666666666, que no es el caso. Como todos saben, el resultado es 0 - C, C ++, Java, Python, todos se comportan igual.

Lo publico en mi página de Facebook y ahora hay un debate sobre si hay un lenguaje de programación en el que se 1/6comporta igual 1.0/6.0.

Pouya
fuente
55
Haskell 1/6 = 0.16666666666666666
t123
PowerShell genera 0.166666666666667, lo que me sorprendió ya que 1 es un número entero. Apuesto a que hay otros lenguajes .NET que generan el valor que esperabas.
JohnL
Hay, en teoría, un número ilimitado de ellos ... Aquí hay otro: Rebol , así como derivados como Orca, Red, etc. >> 1 / 6->== 0.166666666666667
Izkata
Lua hace esto. Solo tiene un tipo de número único , que generalmente es el mismo que el doble de C.
Machado
En Clojure 1/6es en realidad 1/6 (tipo fraccional) que, forzado Double, es 1.66666 ...
kaoD

Respuestas:

17

¿Todos han olvidado a Pascal?

1/6rendimientos 0.1666666...(con la precisión que sea compatible).

1 div 6 rendimientos 0

Es discutible si la regla C es un error. Casi todos los operadores aritméticos de C, donde los operandos son del mismo tipo, producen un resultado del mismo tipo. Hay algo que decir por consistencia.

Además, dado que C está dirigido principalmente a código de nivel de sistema, la mayoría de los programas de C no usan punto flotante en absoluto. En un momento, agregar accidentalmente código de punto flotante a un programa que de otro modo no lo necesitaría podría ser un problema grave. Probablemente ese sea el caso para los pequeños sistemas integrados, que, una vez más, son un objetivo importante para C.

En la mayoría de los programas en C, la división de números enteros truncados es probablemente lo que desea de todos modos.

Si 1 / 6arrojó un resultado de coma flotante en C, entonces:

  • Sería una inconsistencia en el lenguaje.
  • El estándar tendría que hacer una elección arbitraria de qué tipo de punto flotante usar para el resultado ( doublepuede parecer la opción natural, pero es posible que prefiera la precisión adicional de long double)
  • El lenguaje aún tendría que tener una operación para la división de enteros; realizar una adición de punto flotante y luego truncarlo probablemente no sería lo suficientemente bueno.

C podría haber proporcionado operadores separados para los dos tipos de división, pero el segundo punto anterior aún se aplicaría: ¿cuál de los tres tipos de coma flotante se usaría para el resultado? Y dado que es bastante fácil obtener una división de punto flotante si la necesita (use una constante de punto flotante para uno o ambos operandos, o convierta uno o ambos operandos en un tipo de punto flotante), aparentemente no era No lo consideró tan importante.

En la versión de 1974 del manual C (4 años antes de la publicación de la primera edición de K&R), Ritchie ni siquiera menciona la posible confusión:

El binario / operador indica división. Se aplican las mismas consideraciones de tipo que para la multiplicación

que dice que si ambos operandos son de tipo into char, el resultado es de tipo int.

Sí, es una fuente de confusión para algunos programadores de C, especialmente para principiantes, pero C no se destaca por ser muy amigable para los novatos.

Keith Thompson
fuente
55
Pascal. Obtener detalles justo antes de que C se equivoque. ™
Mason Wheeler
Y luego Algol fue una mejora sobre sus sucesores (en el número de los cuales se destacan tanto C como Pascal).
Programador del
Pascal - acertando los detalles (más o menos un factor de 10)
Martin Beckett
1
Originalmente escribí 1.666666..., lo cual es claramente incorrecto. Mi pobre excusa es que el programa de prueba Pascal que escribí impreso1.6666666666666667E-0001
Keith Thompson, el
16

En realidad, este comportamiento se cambió en Python 3 y ahora se comporta como esperabas ( //ahora se usa para la división de enteros).

sepp2k
fuente
Gracias. ¿Algún otro lenguaje de programación tienes en mente? ¿Familia básica tal vez?
Pouya el
1
@Pouya: Este comportamiento es estándar para la familia Pascal. /siempre produce un valor de punto flotante, y divse utiliza un operador separado ( ) para la división de enteros.
Mason Wheeler
13

Fuera de lenguajes destacados, JavaScript. 1.0 / 6.0 = 1/6 = 0.16666666666666666.

No veo esto como sorprendente. Como regla general, si un lenguaje distingue entre tipos numéricos de enteros y de coma flotante, dividir dos enteros producirá un entero truncado en lugar de flotante. Si no es así, lo más probable es que se convierta en operaciones de punto flotante. Este debería ser el comportamiento esperado por parte del programador.

Solo tenga en cuenta que hay cosas adicionales que también podrían estar en juego aquí, como el operador de división de enteros separado ya mencionado o el tipo de conversión implícito.

scrwtp
fuente
66
Esto no es "una regla de oro". Es una regla de C, y un puñado de lenguajes que copiaron ciegamente los errores de C.
Mason Wheeler
44
@MasonWheeler: ¿FORTRAN "copió ciegamente el error de C"? Personalmente, creo que los lenguajes bien diseñados deberían usar operadores separados para la división truncada versus la división aproximada de cantidades enteras (y también, por cierto, para el valor y la igualdad referencial), pero la decisión de diseño en los días de FORTRAN probablemente fue razonable. Eso no significa que cada idioma deba hacer las cosas como FORTRAN hizo en la década de 1950.
supercat
3
Los idiomas de nivel inferior necesitan hacer estas distinciones. Los lenguajes dinámicos / de nivel superior a menudo están mejor sin ellos. Es una compensación de diseño. Aprecio tener solo un gran constructor / tipo de Número tonto en JS, pero imagino que sentiría que le faltaba a JS al intentar escribir un motor 3D de alto rendimiento sin un control de tipo más estricto. JS puede tener una utilidad superpuesta con otros lenguajes, pero no creo que nadie lo considere un goto para escribir material de alto rendimiento más cercano al cromo.
Erik Reppen
2
Las matemáticas fuera de la programación consideran reglas de división solo para enteros.
Erik Reppen
55
@MasonWheeler: La programación no es pura matemática. Matemáticamente, 1/6 es un número racional y no puede representarse exactamente por un número binario de coma flotante. La única representación precisa es como una razón con un denominador seis veces el numerador.
Kevin Cline
7

Hay muchos idiomas donde los ((1/6)*6)resultados son 1, no 0. Por ejemplo, PL / SQL, muchos dialectos BÁSICOS, Lua.

Accidentalmente, en todos esos idiomas, 1/6 resulta en .166666667, o 0.16666666666667 o algo similar. Elegí la variante ((1/6) * 6) == 1 para deshacerme de esas pequeñas diferencias.

usuario281377
fuente
77
Esa no es la pregunta.
Roc Martí
20
Accidentalmente, en todos esos idiomas, 1/6 resulta en .166666667, o 0.16666666666667 o algo similar. Elegí la ((1/6)*6)==1variante para deshacerme de esas pequeñas diferencias, pero parece que sobreestimé las habilidades matemáticas de algunas personas.
user281377
1
@ RocMartí sí, realmente es ...
MattDavey
1
¡Me sorprendería ver que (1.0 / 6.0) * 6 es exactamente igual a 1! El redondeo del resultado de (1.0 / 6.0) conducirá a una pequeña diferencia. (Aunque habrá algunos idiomas que tienen una precisión infinita predeterminada)
Sjoerd
1
@Sjoerd: No es demasiado sorprendente que sea exacto, en realidad. Considere en decimal el escenario de 1/11 * 11 con todos los valores exactos a cinco cifras significativas. El valor de 1/11 es 9.0909 * 10 ^ -2. Multiplique por 11 y uno obtendría 99.9999 * 10 / -2 antes de redondear. Redondea a cinco cifras significativas y el resultado será 1.0000 * 10 ^ 0. Tenga en cuenta que la clave es que la mantisa de 1/6 es "... 0101010101 ...". Si el último bit de una representación es un "1", multiplicándolo por seis y redondeando se obtendrá 1. Si el último bit fuera cero, no lo sería.
supercat
3

Haskell trata 1/6 y 1.0 / 6.0 como idénticamente 0.16666666666666666. También representa 1 / 6.0 y 1.0 / 6 como el mismo valor también.

Esto se debe a que los tipos numéricos básicos en Haskell no son exactamente iguales a otros idiomas. La verdadera división entera es algo ... complicada.

Ingeniero mundial
fuente
2

Sí, lo hace Perl. El one-liner

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

da como resultado la salida de:

0.166666666666667

Creo que PHP funciona de la misma manera.

Editado para agregar: también creo que una condición necesaria (pero no suficiente) 1/6 == 1.0/6.0es que el idioma en cuestión se tipee débilmente.


fuente
¿Por qué diablos sería necesario escribir débilmente (lo que se supone que significa)? Simplemente defina / a (también) división flotante media en el caso de que ambos argumentos sean enteros.
1
@delnan - en.wikipedia.org/wiki/Weak_typing Supongo que podría ser posible tener un lenguaje fuertemente tipado en el que /se sobrecargue automáticamente dependiendo de los tipos de argumentos, pero eso parece una violación del Principio de Menos Asombro para yo ...
2
La escritura débil / fuerte está mal definida (como también lo implica wiki), evítelo y sea específico. Supongo que su definición prohíbe la conversión implícita, pero no el polimorfismo ad-hoc. Si es así, considere Haskell, que no tiene conversiones implícitas pero está bastante bien ejecutado (como en, funciona el 99% del tiempo y puede ser comprendido por los mortales) polimorfismo numérico. ¿Y por qué sería esto asombroso? Sería mucho más sorprendente (por no decir molesto) para mí si tuviera que agregar una serie de puntos a cada instancia de cualquier operador, dependiendo de la precisión exacta que desee.
2
@JackManey Creo que para la mayoría de los recién llegados es mucho más sorprendente que 1/2 sea igual a 0 que si dividir dos enteros da como resultado un doble. Después de que todos los enteros no están cerrados bajo división en matemáticas tampoco. Además, como señala delnan, Haskell es un ejemplo de un lenguaje fuertemente tipado en el que / en dos enteros, no produce un entero. Y Python 3 es otro.
sepp2k
1
El lenguaje Haxe está fuertemente tipado (aunque se infiere) y, sin embargo, no tiene división entera, solo flotante. Ahí vas.
K.Steff
2

En Squeak Smalltalk /en enteros crea objetos Fraction. Entonces, si bien esto no es lo mismo que la división de flotación, aún (1/6)*6devuelve 1.

Cefalópodo
fuente
En todos los derivados de Smalltalk-80 (es decir, casi todos los Smalltalk). El ámbar es una de las excepciones contemporáneas (que es comprensible, ya que se compila en JavaScript).
herby
2

Sí, acabo de comprobar que mi TI-99 / 4A está integrada en TI BASIC . Como trata todas las expresiones numéricas como punto flotante, la operación de división también es punto flotante.

 TI BASIC READY
>PRINT 1/6
  .1666666667

>
Jesse C. Slicer
fuente
2

VB ( VB.Net , VB6 , VBA ...)

El operador de división entera es \

MarkJ
fuente
2

MATLAB Los literales numéricos son dobles por defecto.

>> 1/6
ans =
    0.1667
Dima
fuente
2

Clojure usa fracciones por defecto. No es lo mismo que 1.0 / 6.0, pero puede convertirlo con floato doublecuando lo necesite.

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
fuente
1

Sorprendentemente, parece funcionar correctamente en Windows PowerShell (versión 3) .

PS C:\> 1.0 / 6.0
0.166666666666667

PS C:\> 1/6
0.166666666666667

También parece funcionar en Python 3 como sepp2k mencionado. Los otros dos idiomas que tengo disponibles en REPL, Scala y Ruby, ambos hacen división entera y producen 0.

KChaloux
fuente
0

El lenguaje Rexx siempre produce una respuesta aritméticamente correcta. Por ejemplo: 5/2 = 2.5. Rexx es un gran lenguaje que no se ha utilizado lo suficiente. En teoría, cuando un compilador no puede determinar qué quiere, es mejor hacer las matemáticas correctas, sin embargo, esto puede no ser eficiente. Rexx también proporciona el operador //.

Ninguna posibilidad
fuente