En los idiomas que no permiten guiones bajos en constantes enteras, ¿es una buena práctica crear una constante por mil millones?

39

En los idiomas que no permiten guiones bajos en literales enteros , ¿es una buena idea crear una constante por mil millones? Por ejemplo, en C ++:

size_t ONE_BILLION = 1000000000;

Ciertamente, no deberíamos crear constantes para números pequeños como 100. Pero con 9 ceros, podría decirse que es fácil dejar un cero o agregar uno adicional en un código como este:

tv_sec = timeInNanosec / 1000000000;
tv_nsec = timeInNanosec % 1000000000;
Martin C. Martin
fuente
24
Espero que todos aquí voten por NO . De esa manera, ¡tal vez algún día mi banco transfiera mil millones de dólares a mi cuenta porque un programador no usó una constante y extravió un cero! :)
Reactgular
43
¿Por qué no crear constantes para números pequeños? ¿Qué significa 100? A menos que haya algún contexto, es un número mágico.
Allan
44
@MathewFoscarini En general, los errores pueden ir en cualquier dirección. Pero cuando se trata de su banco, los errores siempre irán en su contra.
emory
23
Considere escribir 1e9, 10^9o 1_000_000_000si el idioma que está usando lo admite.
hammar
55
¿Mil millones a larga o corta escala ?
rvalue

Respuestas:

33

La mayoría de los idiomas presentan algún tipo de notación exponencial. Un millón es 1e6, (lo que significa 1 por 10 a la potencia de 6). Esto básicamente resuelve el problema incluso mejor que la mayoría de las proposiciones aquí.

Sin embargo, en muchos lenguajes tipo C, la notación científica define un tipo de coma flotante , lo cual es desafortunado si realmente necesita un int. Sin embargo, puede escribir fácilmente esa constante para evitar conversiones implícitas en su formulario.

n / int(1e9) se dividiría por mil millones.

En su ejemplo, al tratar con cantidades físicas (tiempo en nanosegundos), generalmente me preguntaría si el entero es el tipo correcto. De hecho, un punto flotante doublepodría ser más adecuado cuando se trata de cantidades medibles (aunque, por supuesto, hay casos en los que preferiría a long long).

wirrbel
fuente
66
Creo que la solución NANOSECONDS_IN_ONE_SECOND es mucho más clara y ordenada
Thomas Bonini,
1
La pregunta era sobre los liberales enteros y propongo usar notación científica. Si hacer esto en el lugar o definiendo una constante es una cuestión de estructurar el código que no se solicitó en la pregunta. Definir una constante agrega abstracción limitada, escribiría una función / macro de conversión para lograr una mejor abstracción
wirrbel
1
¿lanzar un doble muy grande a un int no arriesgaría los problemas típicos de diferencia de redondeo de los números de coma flotante?
Philipp
con tipos enteros de precisión normal, esto no debería ser un problema siempre y cuando utilice un flotante de doble precisión para realizar la conversión. tienes razón cuando usas valores del long longrango.
wirrbel
145

Cree uno llamado NANOSECONDS_IN_ONE_SECOND en su lugar como lo que representa.

O un nombre más corto y mejor si se te ocurre uno.

JohnB
fuente
58
Yo diría que Nanoseconds_Per_Secondesta es, en mi opinión, la respuesta correcta.
KChaloux
8
@Mathew No entiendo tu punto. No hay nada de malo en decir milímetros por metro. Puede estar insinuando que es redundante, en ese nanosegundo SIGNIFICA mil millones de fracciones de segundo, pero no hay nada de malo en declararlo nuevamente. Es como decir 1 + 1 = 2. "x por y" continúa teniendo más sentido cuando x e y son disjuntos, como "unidades por media docena" o "nanosegundos por milisegundo"
Mark Canlas
77
@MathewFoscarini En realidad, no, en este contexto no lo es. Si lo fuera, una constante llamada no NANOSECONDStiene sentido ya que no se puede saber a qué se supone que se aplica. Del mismo modo, NANOSECONDS_PER_MICROSECONDes una constante válida similar que tiene sentido.
Izkata
55
@MathewFoscarini, "milímetros por metro" es una forma de eliminar la unidad en la conversión para obtener el valor bruto. 1mm/1m = 1000, que es exactamente el punto de lo que se está haciendo aquí.
zzzzBov
11
¿Por qué tanto escribir? NS_PER_SECdebería ser obvio para cualquiera que deba lidiar con nanosegundos.
Rex Kerr
67

Las constantes están destinadas a dar significado a los números. No hay ningún significado adicional en ONE_BILLIONa 1000000000. En realidad, lo hace más confuso, porque en diferentes idiomas naturales, ¡mil millones significa algo diferente (ya sea mil millones o un millón)! Si desea escribirlo más corto, hay una buena posibilidad de que su lenguaje de programación permita el uso de notación científica, es decir 1e9. De lo contrario, estoy de acuerdo con @JohnB, que este número realmente significa la cantidad de nanosegundos en un segundo, así que llámalo así.

Thijs van Dien
fuente
9
Es bueno señalar que mil millones en diferentes idiomas significan diferentes cantidades de ceros.
frozenkoi
3
sugeriría cambiar los idiomas regulares a idiomas naturales. regular significa algo más ...
jk.
Diferentes interpretaciones de "mil millones" en idiomas tan buen punto! ¿Por qué no puedo votar tu respuesta dos veces?
DSF
3
No necesitas idiomas diferentes. Ni siquiera necesitas países diferentes. En inglés británico, "mil millones" significa algo diferente antes y después de 1974 en las comunicaciones oficiales (medios de comunicación y gobierno), y todavía existen ambos usos.
Jörg W Mittag
1
" No hay ningún significado adicional en ONE_BILLION para 10000000000. No estoy de acuerdo. (Sugerencia: deliberadamente te cité mal y agregué otro cero; ¿lo habría notado si no lo hubiera mencionado?)
Keith Thompson
27

Para uno o dos usos, usaría la convención:

tv_sec = timeInNanosec / (1000 * 1000 * 1000);
tv_nsec = timeInNanosec % (1000 * 1000 * 1000);

Es perfectamente explicativo, se compila a una constante y es difícil de fastidiar.

Además, es muy útil en casos como:

var Time = 24 * 60 * 60;

donde es fácil ver que estamos hablando de un día en segundos.

Sklivvz
fuente
Esto es lo que suelo hacer. También tiene la ventaja de que no olvidaré que definí NANOSECONDS_IN_ONE_SECOND ayer y defino NANOSECONDS_PER_SECOND hoy. Y tal vez ONE_AMERICAN_BILLION mañana.
Thomas Padron-McCarthy
¿Seguramente 'SecondsInOneDay = 24 * 60 * 60' es aún más fácil?
JBRWilkinson
@JBRWilkinson seguro, mi fragmento inicial estaba usando una clase instance.Time = ..., pero luego lo
detuve
3
En C o C ++, (1000 * 1000 * 1000)es de tipo int, que solo debe tener 16 bits, por lo que puede desbordarse. Puedes escribir (1000L * 1000L * 1000L)para evitar eso.
Keith Thompson
Lo hago mucho. Funciona muy bien.
vy32
10

La longitud del valor no es lo que define si se necesita una constante o no.

Utiliza constantes para evitar números mágicos , no para evitar escribir.

Por ejemplo, estas son constantes perfectamente válidas:

public static final int CLOSE_CURSORS_AT_COMMIT = 1;
public static final int CONCUR_READ_ONLY = 2;
public static final int CONCUR_UPDATABLE = 3;
public static final int FETCH_FORWARD = 4;
public static final int FETCH_REVERSE = 5; 
public static final int FETCH_UNKNOWN = 6;
public static final int HOLD_CURSORS_OVER_COMMIT = 7;
public static final int TYPE_FORWARD_ONLY = 8;
public static final int TYPE_SCROLL_INSENSITIVE = 9;
public static final int TYPE_SCROLL_SENSITIVE = 10;

Utilizar:

public static final int NANOSECS_PER_SECOND = 1000000000;

(los ejemplos de código están en Java, traduzca a su idioma favorito)

Tulains Córdova
fuente
3
+1 Los números nombrados son casi inútiles. El propósito de las constantes es darle significado a esos números. ¿Qué representan? ¿Qué están contando o limitando o coeficiente formalmente nombrado? No es cuál es el valor del recuento.
JustinC
55
Al ver que estamos hablando de Java, haré la observación de que, a partir de Java 7, ¡podemos poner guiones bajos en números para ayudar a la legibilidad!
Nick
2
Esos son ejemplos terribles de constantes válidas. Deberían haber sido enumeraciones, excepto que fueron creadas antes de enumeraciones.
Christoffer Hammarström
@ ChristofferHammarström De hecho, se crearon antes de las enumeraciones, son parte de la clase ResultSet, en el paquete SQL del SDK de Java.
Tulains Córdova
2
@ ChristofferHammarström Son malos porque ahora tenemos enumeraciones pero no por ser insignificantes. Enum no existía cuando se crearon estas clases y para diferenciar entre opciones mutuamente excluyentes como FETCH_FORWARD y FETCH_REVERSE les está dando un valor diferente. El valor no importa, solo el hecho de que son diferentes.
Tulains Córdova
8

¿Un billón americano o europeo?

(o en términos técnicos, mil millones en la escala corta o larga, uno es 1000 millones, el otro es un millón).

Dada esta confusión, entonces diría que sí: tiene sentido definirlo una vez y seguir con él, del mismo modo se aplica a cualquier constante en la que necesite acordar la definición, definirla una vez.

gbjbaanb
fuente
17
"¿Un billón americano o europeo?" - "¿Qué? ¡No lo sé! ¡Ahhhhh!"
Tesserex
En el Reino Unido, al menos, hace mucho tiempo que adoptamos los 1e9 mil millones.
Jack Aidley
1
@Tesserex: bueno, debes saber estas cosas cuando eres un rey, ya sabes.
gbjbaanb
5

Razones para no

En primer lugar, aquí hay una razón para no escribir guiones bajos ni utilizar ningún truco para simularlo: hace que las constantes sean más difíciles de encontrar en el código. Suponga que algún programa exhibe, en algún lugar de su operación, un valor codificado 1500000 para algún parámetro. Quiero saber en qué parte del código fuente del programa ocurre esto, así que busco el código 1500000y no encuentro nada. ¿Por qué? Podría estar en hexadecimal (pero por qué para un número decimal tan redondo). Sin saberlo, la constante en realidad se escribe como 1_500_000. Necesitaba la expresión regular 1_?500_?000.

Guiando Personajes en Comentario

El hecho de que un tipo de ayuda visual no esté disponible, o no deseamos usarlo por la razón anterior, no significa que no podamos aprovechar las dos dimensiones del archivo de texto para crear una ayuda visual alternativa:

foo = bar / 1000000000;
//           --^--^--^  

Con esto podemos convencernos fácilmente de que hay tres grupos de tres ceros. Sin embargo, todavía podemos obtener el código fuente 1000000000y encontrarlo.

Colorear Sintaxis

Se puede hacer un editor de texto con coloración de sintaxis programable para agrupar los dígitos de los grupos en constantes numéricas con colores alternos para una mejor legibilidad. No tenemos que hacer nada en el código.

Preprocesamiento: C, C ++, Objetivo C

Ahora, si realmente queremos algunas comas entre dígitos, en C y C ++ podemos usar algún preprocesamiento:

/* Four digit base TH-ousand constant macro */
/* Condensed using Horner's rule */
#define TH(A,B,C,D) ((((((A) * 1000) + (B)) * 1000) + (C)) * 1000 + D)

tv_sec = nanoseconds / TH(1,000,000,000)

Funciona para números como TH(1,234,567,890).

Una macro similar a TH también puede funcionar con pegado de token en lugar de aritmética. En el preprocesador C, el ##operador binario ("token paste") se puede usar en un cuerpo de macro para pegar dos operandos en un solo token. Uno o ambos operandos pueden ser argumentos macro. La desventaja aquí (creando un riesgo para nosotros) es que si la catetación resultante no es un token válido, el comportamiento es indefinido.

#define TOK4(A, B, C, D) A ## B ## C ## D

Ahora

TOK4(1,000,000,000)       /* produces the single token 1000000000 */
TOK4(1,123,000,000.0E+2)  /* produces the single token 1123000000.0E+2 */
TOK4(pr,in,t,f)           /* produces the token printf */
TOK4(#,*,a,b)             /* undefined behavior, #*ab is not valid token syntax */

Los programas en C que pegan los identificadores y usan los resultados para nombrar variables y funciones globales existen y son horribles para trabajar porque son impermeables a herramientas como GNU id-utils y ctags.

Kaz
fuente
2
+1 para uno de los mejores abusos del preprocesador que he visto. Sin embargo, todavía iría con NSEC_PER_SEC o algo en producción.
Victor
Casi -1 por abusar del preprocesador :)
un CVn
3

Sí, eso suena como una idea razonable. Los errores DIGIT off-by-one son incluso peores que los errores infames off-by-one. Sin embargo, puede crear confusión para que otras personas (incluido su yo futuro) lean el código.

Un nombre más explicativo como NANOSEC_PER_SEC parece bueno, ya que agregaría claridad donde se usa por tiempo. Sin embargo, no tiene sentido usarlo en contextos que no sean el tiempo, y no sería práctico crear 1,000,000,000 por separado para cada situación.

Lo que realmente quieres hacer, por tonto que parezca al principio, es "dividir en segundos". Esto deja a NANO_PER, que no solo es independiente del idioma (10 ^ 9 en América y Europa) sino también independiente de la situación (sin limitación en las unidades), y es fácil de escribir y leer.

MegaWidget
fuente
esta publicación es bastante difícil de leer (muro de texto). ¿Te importaría editarlo en una mejor forma?
mosquito
3

En general, es una mala idea usar constantes escalares para conversiones de unidades, y si te encuentras haciendo constantes para tales cosas, estás haciendo la conversión en demasiados lugares.

Cuando tiene una cantidad de una unidad (por ejemplo, 10 segundos) y desea convertir a otra unidad (es decir, nanosegundos); este es precisamente el momento de usar el sistema de tipos de su idioma para asegurarse de que las unidades se escalen realmente como lo desea.

Hacer su función toma un Nanosecondsparámetro, y proporcionar a los operadores de conversión y / o constructores en este ramo Seconds, Minuteso lo que usted quiera. Aquí es donde su const into #defineo 1e9visto en otras respuestas pertenece.

Esto evita tener variables de unidades ambiguas flotando alrededor de su código; y previene franjas enteras de errores desde donde se aplicó la multiplicación / división incorrecta, o ya se aplicó, o la cantidad fue en realidad la distancia en lugar del tiempo, o ...

Además, en tales clases es bueno hacer una construcción a partir de escalares privados y usar un "MakeSeconds (int)" estático o similar para desalentar el uso descuidado de números opacos.

Más específicamente a su ejemplo, en C ++, consulte Boost.Chrono .

rvalue
fuente
1
+ Como mínimo, use un tipo común con un factor de escala o de compensación de una base, muy similar a la zona horaria a menudo difamada.
JustinC
1

Personalmente, no consideraría una buena práctica crear una constante a menos que sea una constante. Si va a estar en varios lugares y tenerlo definido en la parte superior del archivo para modificación / prueba va a ser útil, entonces absolutamente.

Si es solo porque es incómodo de escribir? entonces no.

Personalmente, si obtuve el código de otra persona que tenía una constante definida, generalmente considero que este es un aspecto importante del código. Por ejemplo, tcp mantiene temporizadores vivos, número máximo de conexiones permitidas. Si tuviera que depurarlo, probablemente le prestaría mucha atención innecesaria tratando de averiguar por qué / dónde se está utilizando.

Simon McLoughlin
fuente
Entiendo el chiste, pero si los programadores del banco tuvieran que hacer una constante por cada número que pudieras transferir, el software sería gigantesco, inmanejable y lento. Solo podía imaginar cómo sería eso, imagina que me digan que tomaría 3 días hábiles transferir dinero a ... ¡OH DIOS MÍO, ESO ES!
Simon McLoughlin
Mi banco
demora
1
@MathewFoscarini banqueros usan Excel, no necesitan programadores;)
Mateusz
@Simon Dependiendo del idioma y el compilador, las constantes deben optimizarse en el código, incurriendo en una pequeña sobrecarga. Entiendo su punto, pero las constantes se pueden usar donde sea que usar un nombre en lugar de un número mágico ayudaría a la legibilidad del código.
Steven
La incómoda lectura es mucho más problemática que la incómoda de escribir.
Alb
0

Cuando piense por qué escribió "1 mil millones" en lugar de "1000000000" en el título de su pregunta, se dará cuenta de por qué la respuesta es sí.

Alba
fuente
0

No cree una constante para sus grandes literales. Necesitaría una constante para cada literal, que es (en mi opinión) una broma completa. Si necesita desesperadamente aclarar sus literales sin la ayuda de elementos como el resaltado de sintaxis, podría (aunque no lo haría) crear funciones o macros para hacer su vida "más fácil":

#define SPLIT3(x, y, z) x##y##z

int largeNumber1 = SPLIT3(123,456,789);
int largeNumber2 = 123456789;
Thomas Eding
fuente
0

Yo haría esto:

const int Million = 1000 * 1000;
const int Billion = 1000 * Million;

o

const int SciMega = 1000 * 1000; const int SciGiga = 1000 * SciMega;

Con respecto al número de nanosegundos por segundo: nano es el "inverso" de giga.

Kilo  Mega  Giga   etc.
10^3  10^6  10^9
Milli Micro Nano   etc.
10^-3 10^-6 10^-9

Tenga en cuenta el "Sci" - para científicos, como en las computadoras, los significados de kilo, mega, giga, etc. son diferentes: 1024 (2 ^ 10), 1024 * 1024 (2 ^ 20), etc. 2 megabytes no son 2,000,000 bytes .

ACTUALIZADOR El comentarista señaló que existen términos especiales para exponentes digitales de 2: http://en.wikipedia.org/wiki/Mebibyte

Sr. TA
fuente
"2 megabytes no son 2,000,000 bytes". Pregúntele al fabricante de discos duros de disco giratorio que desee. (No el votante, por cierto)
un CVn
@michaelkjorling esta es una pregunta de programación, no una ética comercial o de marketing. Estoy de acuerdo con los discos duros, pero ese es un tema diferente. Y ¡ay sobre los votos negativos!
Sr. TA
1
En realidad, 2 Megabytes son 2,000,000 bytes. 2 Mebibytes son 2,097,152 bytes. Ver en.wikipedia.org/wiki/Mebibyte
vy32
@ vy32 gracias, nunca he oído hablar de eso antes. Actualizaré mi respuesta para reflejar eso.
Sr. TA
@ Mr.TA, no hay problema! ¡Estamos trabajando duro para que la informática cumpla con las Unidades SI! Únete al club.
vy32