Cero como una constante?

15

Me he encontrado con este lenguaje de programación recientemente:

const float Zero = 0.0;

que luego se usa en las comparaciones:

if (x > Zero) {..}

¿Alguien puede explicar si esto es realmente más eficiente, legible o mantenible que:

if (x > 0.0) {..}

NOTA: Puedo pensar en otras razones para definir esta constante, solo me pregunto sobre su uso en este contexto.

NWS
fuente
31
¿Los desarrolladores planean portar el código a un universo donde las leyes de las matemáticas son diferentes?
vaughandroid
66
En serio, no puedo pensar en una sola buena razón para esto. Las únicas explicaciones que se me ocurren son estándares de codificación excesivamente entusiastas, o algunos desarrolladores que han escuchado "los números mágicos son malos" pero no entienden por qué (o qué constituiría un número mágico) ...
vaughandroid
@Baqueta -Un universo alternativo? ¡Creo que ya vivían allí! En cuanto a los números mágicos, estoy de acuerdo, sin embargo, uso la regla general de que todo, excepto 0 y 1, debe hacerse constante.
NWS
1
Si xtiene tipo float, entonces x > 0.0fuerza la promoción a double, que podría ser menos eficiente. Sin embargo, esa no es una buena razón para usar una constante con nombre, solo para asegurarse de que sus constantes tengan el tipo correcto (por ejemplo 0f, float(0)o decltype(x)(0)).
Mike Seymour
1
@JoSo: para ser justos, el tipo de 13.37no es float, es double. Entonces, si querías un float, es concebible que tu tutor fuera correcto. En algunos contextos (p. Ej., La asignación a un flotante) 13.37se convertirá implícitamente al floatque deseaba, y en otros contextos (p. Ej., Deducción de tipo de plantilla) no lo será, mientras que static const floatsiempre comienza como el tipo que deseaba. Por lo tanto, más seguro de tipo. Eso sí, así sería 13.37f! Sin embargo, hay otras razones para evitar la macro que la "seguridad de tipo", por lo que es muy probable que el tutor le haya dado un argumento pobre.
Steve Jessop

Respuestas:

29

Los posibles motivos son el almacenamiento en caché, el nombre o el tipo de forzamiento

Almacenamiento en caché (no aplicable)

Desea evitar el costo de crear un objeto durante el acto de comparación. En Java, un ejemplo sería

BigDecimal zero = new BigDecimal ("0.0");

Esto implica un proceso de creación bastante pesado y se sirve mejor utilizando el método estático proporcionado:

BigDecimal zero = BigDecimal.ZERO;

Esto permite comparaciones sin incurrir en un costo repetido de creación, ya que la JVM almacena en caché previamente el BigDecimal durante la inicialización.

En el caso de lo que ha descrito, un primitivo está realizando el mismo trabajo. Esto es en gran medida redundante en términos de almacenamiento en caché y rendimiento.

Nombramiento (poco probable)

El desarrollador original está intentando proporcionar una convención de nomenclatura uniforme para valores comunes en todo el sistema. Esto tiene algún mérito, especialmente con valores poco comunes, pero algo tan básico como cero solo vale la pena en el caso del caso de almacenamiento en caché anterior.

Tipo de forzamiento (lo más probable)

El desarrollador original está intentando forzar un tipo primitivo particular para garantizar que las comparaciones se emitan a su tipo correcto y posiblemente a una escala particular (número de lugares decimales). Esto está bien, pero el nombre simple "cero" probablemente sea un detalle insuficiente para este caso de uso con ZERO_1DP como una expresión más apropiada de la intención.

Gary Rowe
fuente
2
+1 para forzar tipo. Agregaré eso en lenguajes como C ++ que permiten la sobrecarga del operador, la definición de una constante y el uso de typedefmantendrían el tipo de variable exactamente en un lugar y permitirían cambiarlo sin tener que alterar el código.
Blrfl
3
Lo más probable es que forzar el tipo no sea lo que intentaban, sin embargo, esta es la mejor explicación de por qué podría hacerse.
NWS
77
Para forzar el tipo, probablemente prefiera usarlo 0.0f.
Svish
El tipo de forzado a veces puede ser útil en vb.net, donde realizar operadores bit a bit en bytes produce un resultado de byte. Decir byteVar1 = byteVar2 Or CB128parece un poco mejor que byteVar1 = byteVar2 Or CByte(128). Por supuesto, tener un sufijo numérico apropiado para bytes sería aún mejor. Dado que C # promueve los operandos de bit a bit para los operadores, intincluso cuando se garantice que el resultado encajará en a byte, el problema no es tan relevante allí.
supercat
No estoy seguro de tener el nombre constante como Cero para '0' pero a veces ayuda en la legibilidad del código; por ejemplo, tener esta constante para cero - "ROOT_TYPE_ID = 0" ayudará a escribir una declaración como if (id! = ROOT_TYPE_ID) {..}
Tech Junkie
6

Es por "molestar a las herramientas"

Una posible razón que no veo en la lista aquí es porque muchas herramientas de calidad señalan el uso de números mágicos . A menudo es una mala práctica tener números mágicos en un algoritmo sin hacerlos claramente visibles para un cambio posterior, especialmente si están duplicados en varios lugares del código.

Por lo tanto, si bien estas herramientas tienen razón al señalar estos problemas, a menudo generan falsos positivos para situaciones en las que estos valores son inofensivos y es más probable que sean estáticos o simplemente sean valores de inicialización.

Y cuando eso sucede, a veces te enfrentas a la elección de:

  • marcarlos como falsos positivos, si la herramienta lo permite (generalmente con un comentario con formato especial, lo cual es molesto para las personas que NO usan la herramienta)
  • o extraer estos valores a constantes, si importa o no.

Sobre rendimiento

Depende del lenguaje, supongo, pero esto es bastante común en Java y no tiene ningún impacto en el rendimiento, ya que los valores están en línea en el momento de la compilación si son constantes reales static final. No tendría un impacto en C o C ++ si se declaran como constantes o incluso como macros de preprocesador.

haylem
fuente
5

Esto puede tener sentido ya que define explícitamente Zeroque es de tipo float.

Al menos en C y C ++, el valor 0.0es de tipo double, mientras que el equivalente floates 0.0f. Por lo tanto, asumir xque comparas también es siempre un floatdicho

x > 0.0

mientras que en realidad promueve xpara doubleque coincida con el tipo de lo 0.0que podría conducir a problemas (especialmente con pruebas de igualdad) La comparación sin conversión sería, por supuesto,

x > 0.0f

que hace lo mismo que

float Zero = 0.0; // double 0.0 converted to float  
x > Zero

Sin embargo, creo que sería mucho más útil habilitar advertencias de conversiones en el compilador en lugar de hacer que los usuarios escriban código incómodo.

Benjamin Bannier
fuente
1

En primer lugar, aquí cero se define como float, no int. Por supuesto, esto no afecta nada en la comparación, pero en otros casos cuando se usa esta constante, puede hacer la diferencia.

No veo otra razón por la cual Zerose declara una constante aquí. Es solo un estilo de codificación, y es mejor seguir el estilo si se usa en cualquier otro lugar de ese programa.

superM
fuente
1

Es casi seguro que sea exactamente eficiente durante la ejecución (a menos que su compilador sea muy primitivo) y muy ligeramente menos eficiente durante la compilación.

En cuanto a si eso es más legible que x > 0... recuerde que hay personas que honestamente, genuinamente, piensan que COBOL fue una gran idea y un placer trabajar con ellos, y luego hay personas que piensan exactamente lo mismo sobre C. (El rumor ha ¡es que incluso existen algunos programadores con la misma opinión sobre C ++!) En otras palabras, no vas a llegar a un acuerdo general sobre este punto, y probablemente no valga la pena pelear.

Kilian Foth
fuente
0

[es] esto es realmente más eficiente, legible o mantenible que:

if (x > 0.0) {..}

Si estaba escribiendo código genérico (es decir, no específico del tipo), entonces muy posiblemente. Una zero()función podría aplicarse a cualquier tipo algebraico, o cualquier tipo que sea una adición de grupo wrt. Podría ser un número entero, podría ser un valor de coma flotante, incluso podría ser una función si su variable es, por ejemplo, una función dentro de un espacio lineal (por ejemplo, x es una función lineal de la forma z -> a_x * z + b_x) y luego zero()proporciona la función con a y b siendo ambos zero()del tipo subyacente.

Por lo tanto, esperaría dicho código en, por ejemplo, C ++ posiblemente (aunque a zero()no es muy común AFAIK), o en Julia, y tal vez en otros idiomas.

einpoklum
fuente