Nelle lingue che non consentono il trattino di sottolineatura nelle costanti intere, è una buona pratica creare una costante per 1 miliardo?

38

Nelle lingue che non consentono underscore in letterali interi , è una buona idea creare una costante per 1 miliardo? per esempio. in C ++:

size_t ONE_BILLION = 1000000000;

Certamente, non dovremmo creare costanti per numeri piccoli come 100. Ma con 9 zeri, è discutibilmente facile lasciare uno zero o aggiungerne uno in codice come questo:

tv_sec = timeInNanosec / 1000000000;
tv_nsec = timeInNanosec % 1000000000;
    
posta Martin C. Martin 24.05.2013 - 18:05
fonte

13 risposte

32

La maggior parte delle lingue presenta una sorta di notazione esponenziale. Un milione è 1e6 , (che significa 1 x 10 alla potenza di 6). Questo in sostanza risolve il problema anche meglio della maggior parte delle proposizioni qui.

In molte lingue simili a C, la notazione scientifica sta tuttavia definendo un tipo a virgola mobile , il che è sfortunato se hai davvero bisogno di un int. Tuttavia, puoi facilmente digitare tale costante per evitare conversioni implicite nel tuo formulare.

n / int(1e9) dividerebbe per un miliardo.

Nel tuo esempio, che riguarda quantità fisiche (tempo in nanosecondi), generalmente mi chiedo se il numero intero sia corretto. Infatti, un punto fluttuante double potrebbe essere più adatto quando si gestiscono quantità misurabili (sebbene ci siano naturalmente casi in cui si preferirebbe un long long ).

    
risposta data 25.05.2013 - 11:42
fonte
145

Crea uno chiamato NANOSECONDS_IN_ONE_SECOND invece che quello che rappresenta.

O un nome più breve e migliore se puoi pensarne uno.

    
risposta data 24.05.2013 - 18:21
fonte
67

Le costanti hanno lo scopo di dare un significato ai numeri. Non c'è alcun significato aggiuntivo in ONE_BILLION in 1000000000 . In realtà, lo rende più confuso, perché in diverse lingue naturali, un miliardo significa qualcosa di diverso (o un milione o un milione di milioni)! Se vuoi scriverlo più breve, c'è una buona probabilità che il tuo linguaggio di programmazione consenta l'uso della notazione scientifica, ovvero 1e9 . Altrimenti, sono d'accordo con @JohnB, che questo numero significa davvero il numero di nanosecondi in un secondo, quindi chiamalo così.

    
risposta data 24.05.2013 - 18:22
fonte
27

Per uno o due usi, userei la convenzione:

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

È perfettamente auto esplicativo, viene compilato su una costante ed è difficile sbagliare.

Inoltre, è molto utile in casi come:

var Time = 24 * 60 * 60;

dove è facile vedere che stiamo parlando di un giorno in secondi.

    
risposta data 24.05.2013 - 23:42
fonte
10

La lunghezza del valore non è ciò che definisce se una costante è necessaria o meno.

Utilizza le costanti per evitare i numeri magici , non per evitare di digitare.

Ad esempio, queste sono costanti perfettamente valide:

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;

Usa:

public static final int NANOSECS_PER_SECOND = 1000000000;

(esempi di codice sono in Java, traduci nella tua lingua preferita)

    
risposta data 24.05.2013 - 20:18
fonte
8

Un miliardo americano o europeo?

(o in termini tecnici, un miliardo a breve o lungo termine: uno è di 1000 milioni, l'altro è un milione di milioni).

Data questa confusione, direi di sì - ha senso definirlo una volta e tenerlo con sé, allo stesso modo si applica a qualsiasi costante devi concordare la definizione - definirla una volta.

    
risposta data 24.05.2013 - 21:13
fonte
5

Motivi non a

In primo luogo, ecco una ragione per non scrivere alcun trattino basso o usare alcun trucco per simularlo: rende le costanti più difficili da trovare nel codice. Supponiamo che qualche programma presenti, da qualche parte nella sua operazione, un valore hard-coded 1500000 per alcuni parametri. Voglio sapere dove si trova effettivamente il codice sorgente del programma, quindi grep il codice per 1500000 e non trovo nulla. Perché? Potrebbe essere in esadecimale (ma perché per un numero decimale rotondo tale). A mia insaputa, la costante è in realtà scritta come 1_500_000 . Mi serviva la regex 1_?500_?000 .

Guida ai caratteri nel commento

Solo perché un tipo di aiuto visivo non è disponibile, o non vogliamo usarlo per la ragione sopra, non significa che non possiamo sfruttare le due dimensioni del file di testo per creare un visual alternativo aiuto:

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

Con questo possiamo facilmente convincerci che ci sono tre gruppi di tre zeri. Tuttavia, possiamo ancora grep il codice sorgente per 1000000000 e trovarlo.

Colorazione sintassi

È possibile creare un editor di testo con colorazione sintassi programmabile per raggruppare i numeri di cifre in costanti numeriche con colori alternati per una migliore leggibilità. Non dobbiamo fare nulla nel codice.

Pre-elaborazione: C, C ++, obiettivo C

Ora, se vogliamo davvero alcune virgole tra le cifre, in C e C ++ possiamo usare un po 'di pre-elaborazione:

/* 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)

Funziona per numeri come TH(1,234,567,890) .

Una macro simile a TH può anche funzionare con l'aggiunta di token piuttosto che con l'aritmetica. Nel preprocessore C, l'operatore binario ## ("token paste") può essere utilizzato in un corpo macro per incollare insieme due operandi in un singolo token. Uno o entrambi gli operandi possono essere argomenti macro. Il lato negativo qui (creando un rischio per noi) è che se la catenaria risultante non è un token valido, il comportamento non è definito.

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

Ora

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 */

I programmi C che incollano gli identificatori e usano i risultati per nominare le variabili e le funzioni globali esistono e sono terribili su cui lavorare perché sono impermeabili a strumenti come GNU id-utils e ctags.

    
risposta data 25.05.2013 - 04:32
fonte
3

Sì, sembra un'idea ragionevole. Gli errori DIGIT off-by-one sono persino peggiori dei famigerati errori off-by-one. Anche se, potrebbe creare confusione per le altre persone (incluso il tuo futuro sé) per leggere il codice.

Un nome più esplicativo come NANOSEC_PER_SEC sembra buono, in quanto aggiungerebbe chiarezza dove è usato per il tempo. Tuttavia, non ha senso utilizzarlo in contesti diversi dal tempo, e sarebbe impraticabile creare un separato 1.000.000.000 per ogni situazione.

Quello che vuoi veramente fare, sciocco come sembra a prima vista, è "dividi su sec". Questo lascia NANO_PER, che non è solo indipendente dalla lingua (10 ^ 9 in America e in Europa) ma anche indipendente dalla situazione (nessuna limitazione sulle unità), ed è facile da scrivere e leggere.

    
risposta data 24.05.2013 - 22:08
fonte
3

In generale è una cattiva idea utilizzare le costanti scalari per le conversioni di unità e, se ti trovi a fare costanti per tali cose, stai facendo la conversione in troppi punti.

Quando hai una quantità di un'unità (diciamo, 10 secondi) e vuoi convertirla in un'altra unità (cioè nanosecondi); questo è esattamente il momento di usare il sistema di tipi della tua lingua per assicurarti che le unità siano effettivamente ridimensionate come desideri.

Rendi la tua funzione un parametro Nanoseconds e fornisci operatori di conversione e / o costruttori in quella classe per Seconds , Minutes o what-have-you. Qui è dove il tuo const int o #define o 1e9 visto in altre risposte appartiene.

Questo evita di avere variabili di unità ambigue che galleggiano attorno al tuo codice; e previene intere fasce di errori da cui è stato applicato il moltiplicatore / divario sbagliato, oppure è stato già applicato, oppure la quantità era in realtà la distanza anziché il tempo o ...

Inoltre, in tali classi è bene fare costruzioni da semplici scalars private e usare un "MakeSeconds (int)" statico o simili per scoraggiare l'uso sciatto di numeri opachi.

Più specificamente al tuo esempio, in C ++ dai un'occhiata a Boost.Chrono .

    
risposta data 26.05.2013 - 08:37
fonte
1

Personalmente non considererei una buona pratica creare una costante a meno che non debba essere una costante. Se il suo essere in più posti e averlo definito nella parte superiore del file per la modifica / o il test sarà utile quindi assolutamente.

Se è solo perché è scomodo da digitare? allora no.

Personalmente se ottengo il codice di qualcun altro che ha una costante definita, generalmente considero questo un aspetto importante del codice. Per esempio. tcp keep alive timer, numero massimo di connessioni consentite. Se dovessi eseguire il debug, probabilmente dovrei prestare molta attenzione a questo tentativo cercando di capire perché / dove viene usato.

    
risposta data 24.05.2013 - 18:15
fonte
0

Quando pensi al motivo per cui hai scritto "1 miliardo" anziché "1000000000" nel titolo della tua domanda, ti renderai conto del perché la risposta è sì.

    
risposta data 25.05.2013 - 01:11
fonte
0

Non creare una costante per i tuoi grandi letterali. Avresti bisogno di una costante per ciascuno di questi letterali, che è (secondo me) uno scherzo completo. Se hai un disperato bisogno di rendere i tuoi letterali più chiari senza l'aiuto di cose come l'evidenziazione della sintassi, potresti (anche se non lo farei) creare funzioni o macro per rendere la tua vita "più facile":

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

int largeNumber1 = SPLIT3(123,456,789);
int largeNumber2 = 123456789;
    
risposta data 03.06.2013 - 20:08
fonte
0

Lo farei:

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

o

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

Per quanto riguarda il numero di nanosecondi al secondo: nano è l'inverso di giga.

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

Nota la "Sci": per quanto riguarda i computer, i significati di kilo, mega, giga, ecc. sono diversi: 1024 (2 ^ 10), 1024 * 1024 (2 ^ 20), ecc. 2 megabyte non sono 2.000.000 di byte.

UPDATE Il commentatore ha sottolineato che esistono termini speciali per esponenti digitali di 2: link

    
risposta data 25.05.2013 - 04:56
fonte

Leggi altre domande sui tag