Por ejemplo, quiero mostrar una lista de botones de 0,0.5, ... 5, que salta por cada 0.5. Utilizo un bucle for para hacer eso, y tengo un color diferente en el botón STANDARD_LINE:
var MAX=5.0;
var DIFF=0.5
var STANDARD_LINE=1.5;
for(var i=0;i<=MAX;i=i+DIFF){
button.text=i+'';
if(i==STANDARD_LINE){
button.color='red';
}
}
En este caso, no debería haber errores de redondeo ya que cada valor es exacto en IEEE 754. Pero estoy luchando si debería cambiarlo para evitar la comparación de igualdad de punto flotante:
var MAX=10;
var STANDARD_LINE=3;
for(var i=0;i<=MAX;i++){
button.text=i/2.0+'';
if(i==STANDARD_LINE/2.0){
button.color='red';
}
}
Por un lado, el código original es más simple y reenviado a mí. Pero hay una cosa que estoy considerando: ¿i == STANDARD_LINE engaña a los compañeros de equipo junior? ¿Oculta el hecho de que los números de coma flotante pueden tener errores de redondeo? Después de leer los comentarios de esta publicación:
Parece que hay muchos desarrolladores que no saben que algunos números flotantes son exactos. ¿Debo evitar las comparaciones de igualdad de números flotantes incluso si es válido en mi caso? ¿O estoy pensando demasiado en esto?
i
solo serán números enteros en la segunda lista. Intenta eliminar el segundo/2.0
.button
no cambia en ninguna parte de tu circuito. ¿Cómo se accede a la lista de botones? ¿Por índice en matriz o algún otro mecanismo? Si es por acceso de índice a una matriz, este es otro argumento a favor de cambiar a enteros.Respuestas:
Siempre evitaría las sucesivas operaciones de punto flotante a menos que el modelo que estoy computando los requiera. La aritmética de punto flotante no es intuitiva para la mayoría y es una fuente importante de errores. ¡Y contar los casos en los que causa errores de aquellos en los que no es una distinción aún más sutil!
Por lo tanto, el uso de flotantes como contadores de bucle es un defecto que espera suceder y requeriría al menos un comentario de fondo grueso que explique por qué está bien usar 0.5 aquí, y que esto depende del valor numérico específico. En ese punto, reescribir el código para evitar contadores flotantes probablemente sea la opción más legible. Y la legibilidad está al lado de la corrección en la jerarquía de los requisitos profesionales.
fuente
DIFF must be an exactly-representable double that evenly divides STANDARD_LINE
. Si no desea escribir ese comentario (y confiar en que todos los futuros desarrolladores sepan lo suficiente sobre el punto flotante binario IEEE754 para comprenderlo), no escriba el código de esta manera. es decir, no escriba el código de esta manera. Especialmente porque probablemente no sea aún más eficiente: la adición de FP tiene una latencia más alta que la suma de enteros, y es una dependencia transportada en bucle. Además, los compiladores (¿incluso los compiladores JIT?) Probablemente funcionen mejor para hacer bucles con contadores enteros.Como regla general, los bucles deben escribirse de tal manera que se piense hacer algo n veces. Si está utilizando índices de coma flotante, ya no se trata de hacer algo n veces sino de ejecutarlo hasta que se cumpla una condición. Si esta condición es muy similar a la
i<n
que esperan muchos programadores, entonces el código parece estar haciendo una cosa cuando en realidad está haciendo otra que los programadores pueden malinterpretar fácilmente.Es algo subjetivo, pero en mi humilde opinión, si puede reescribir un bucle para usar un índice entero para recorrer un número fijo de veces, debe hacerlo. Considere la siguiente alternativa:
El ciclo funciona en términos de números enteros. En este caso,
i
es un número entero ySTANDARD_LINE
se coacciona a un número entero también. Esto, por supuesto, cambiaría la posición de su línea estándar si ocurriera un redondeo y lo mismoMAX
, por lo que aún debe esforzarse por evitar el redondeo para una representación precisa. Sin embargo, aún tiene la ventaja de cambiar los parámetros en términos de píxeles y no de números enteros sin tener que preocuparse por la comparación de los puntos flotantes.fuente
i
, ySTANDARD_LINE
sólo se verá como números enteros. No hay coerción en absoluto, yDIFF
,MAX
ySTANDARD_LINE
todos son soloNumber
s.Number
Los s usados como enteros deberían estar seguros debajo2**53
, sin embargo, todavía son números de coma flotante.Estoy de acuerdo con todas las otras respuestas de que usar una variable de bucle no entero es generalmente un mal estilo, incluso en casos como este, donde funcionará correctamente. Pero me parece que hay otra razón por la cual es un mal estilo aquí.
Su código "sabe" que los anchos de línea disponibles son precisamente los múltiplos de 0.5 desde 0 hasta 5.0. ¿Deberia? Parece que es una decisión de la interfaz de usuario que podría cambiar fácilmente (por ejemplo, tal vez desee que los espacios entre los anchos disponibles se hagan más grandes a medida que los anchos lo hacen. 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0 o algo así).
Su código "sabe" que los anchos de línea disponibles tienen representaciones "agradables" tanto como números de coma flotante como decimales. Eso también parece algo que podría cambiar. (Es posible que desee 0.1, 0.2, 0.3, ... en algún momento).
Su código "sabe" que el texto para poner en los botones es simplemente en lo que Javascript convierte esos valores de punto flotante. Eso también parece algo que podría cambiar. (Por ejemplo, quizás algún día desee anchos como 1/3, que probablemente no quiera mostrar como 0.33333333333333 o lo que sea. O tal vez quiera ver "1.0" en lugar de "1" para mantener la coherencia con "1.5" .)
Todos estos me parecen manifestaciones de una sola debilidad, que es una especie de mezcla de capas. Esos números de punto flotante son parte de la lógica interna del software. El texto que se muestra en los botones es parte de la interfaz de usuario. Deberían estar más separados de lo que están en el código aquí. Nociones como "¿cuál de estos es el valor predeterminado que debe resaltarse?" son asuntos relacionados con la interfaz de usuario, y probablemente no deberían estar vinculados a esos valores de punto flotante. Y su bucle aquí es realmente (o al menos debería ser) un bucle sobre botones , no sobre anchos de línea . Escrito de esa manera, la tentación de usar una variable de bucle que toma valores no enteros desaparece: solo estarías usando enteros sucesivos o un for ... in / for ... of loop.
Mi opinión es que la mayoría de los casos en los que uno podría verse tentado a recorrer números no enteros son así: hay otras razones, totalmente ajenas a los problemas numéricos, por las cuales el código debe estar organizado de manera diferente. (No todos los casos; me imagino que algunos algoritmos matemáticos podrían expresarse de manera más clara en términos de un bucle sobre valores no enteros).
fuente
Un código de olor está usando flotadores en bucle como ese.
El bucle se puede hacer de muchas maneras, pero en el 99.9% de los casos, debe apegarse a un incremento de 1 o definitivamente habrá confusión, no solo por los desarrolladores junior.
fuente
Sí, quieres evitar esto.
Los números de coma flotante son una de las mayores trampas para el programador desprevenido (lo que significa, en mi experiencia, casi todos). Desde depender de las pruebas de igualdad de punto flotante, hasta representar el dinero como punto flotante, todo es un gran atolladero. Agregar una carroza sobre la otra es uno de los mayores delincuentes. Hay volúmenes enteros de literatura científica sobre cosas como esta.
Use números de coma flotante exactamente en los lugares donde sean apropiados, por ejemplo, al hacer cálculos matemáticos reales donde los necesita (como trigonometría, gráficos de funciones de trazado, etc.) y tenga mucho cuidado al realizar operaciones en serie. La igualdad está justo afuera. El conocimiento sobre qué conjunto particular de números es exacto según los estándares IEEE es muy arcano y nunca dependería de él.
En su caso, no habrá , por la Ley Murphys, llegado el punto en el que la gestión quiere que usted no tiene 0.0, 0.5, 1.0 ... pero 0.0, 0.4, 0.8 ... o lo que sea; usted será inmediatamente molestado, y su programador junior (o usted mismo) lo depurará durante mucho tiempo hasta que encuentre el problema.
En su código particular, de hecho tendría una variable de bucle entero. Representa el
i
botón th, no el número de ejecución.Y que probablemente, en aras de la claridad adicional, no escribir
i/2
, peroi*0.5
lo que lo hace muy claro lo que está pasando.Nota: como se señaló en los comentarios, JavaScript en realidad no tiene un tipo separado para enteros. Pero se garantiza que los números enteros de hasta 15 dígitos sean precisos / seguros (consulte https://www.ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer ), por lo tanto, para argumentos como este ("es más confuso / propenso a errores para trabajar con enteros o no enteros ") esto está cerca de tener un tipo separado" en espíritu "; en el uso diario (bucles, coordenadas de pantalla, índices de matriz, etc.) no habrá sorpresas con números enteros representados
Number
como JavaScript.fuente
No creo que ninguna de sus sugerencias sea buena. En cambio, introduciría una variable para la cantidad de botones en función del valor máximo y el espaciado. Luego, es lo suficientemente simple como para recorrer los índices del botón.
Puede ser más código, pero también es más legible y más robusto.
fuente
Puede evitar todo calculando el valor que está mostrando en lugar de usar el contador de bucle como valor:
fuente
La aritmética de coma flotante es lenta y la aritmética de enteros es rápida, por lo que cuando uso coma flotante, no la usaría innecesariamente cuando se pueden usar enteros. Es útil pensar siempre en números de coma flotante, incluso constantes, como aproximados, con algún pequeño error. Es muy útil durante la depuración reemplazar los números de coma flotante nativos con objetos de coma flotante más / menos donde trata cada número como un rango en lugar de un punto. De esa manera, descubrirá imprecisiones de crecimiento progresivo después de cada operación aritmética. Entonces, "1.5" debe considerarse como "algún número entre 1.45 y 1.55", y "1.50" debe considerarse como "algún número entre 1.495 y 1.505".
fuente