Czy w językach, które nie zezwalają na podkreślenia w liczbach całkowitych, dobrą praktyką jest tworzenie stałej dla 1 miliarda?

39

Czy w językach, w których niedozwolone jest podkreślanie literałów całkowitych , dobrym pomysłem jest utworzenie stałej dla 1 miliarda? np. w C ++:

size_t ONE_BILLION = 1000000000;

Z pewnością nie powinniśmy tworzyć stałych dla małych liczb, takich jak 100. Ale przy 9 zerach można łatwo pominąć zero lub dodać dodatkową w kodzie:

tv_sec = timeInNanosec / 1000000000;
tv_nsec = timeInNanosec % 1000000000;
Martin C. Martin
źródło
24
Mam nadzieję, że wszyscy tutaj głosują na NIE . W ten sposób może pewnego dnia mój bank przelej miliard dolarów na moje konto, ponieważ programista nie użył stałej i źle wprowadził zero! :)
Reactgular
43
Dlaczego nie stworzyć stałych dla małych liczb? Co oznacza 100? O ile nie ma kontekstu, jest to magiczna liczba.
Allan
4
@MathewFoscarini Ogólnie błędy mogą iść w obie strony. Ale jeśli chodzi o twój bank, błędy zawsze będą ci przeszkadzać.
emory
23
Rozważmy piśmie 1e9, 10^9lub 1_000_000_000jeśli języka używasz obsługuje.
hammar
5
Długa czy krótka miliard?
rvalue

Odpowiedzi:

33

Większość języków ma zapis wykładniczy. Milion to 1e6(1 razy 10 do potęgi 6). Zasadniczo rozwiązuje to problem jeszcze lepiej niż większość propozycji tutaj.

Jednak w wielu językach podobnych do C notacja naukowa definiuje typ zmiennoprzecinkowy , co jest niefortunne, jeśli naprawdę potrzebujesz int. Możesz jednak z łatwością wpisać tę stałą, aby uniknąć niejawnych konwersji w formule.

n / int(1e9) podzieliłby się przez miliard.

W twoim przykładzie, dotyczącym wielkości fizycznych (czas w nanosekundach), ogólnie zadałbym sobie pytanie, czy liczba całkowita jest właściwym typem. W rzeczywistości zmiennoprzecinkowa doublemoże być lepiej dopasowana do mierzalnych wielkości (chociaż są oczywiście przypadki, w których wolisz a long long).

wirrbel
źródło
6
Myślę, że rozwiązanie NANOSECONDS_IN_ONE_SECOND jest znacznie bardziej przejrzyste i schludne
Thomas Bonini
1
Pytanie dotyczyło całkowitych liberałów i proponuję użyć notacji naukowej. To, czy zrobić to na miejscu, czy przez zdefiniowanie stałej, jest kwestią struktury kodu, o którą nie pytano w pytaniu. Zdefiniowanie stałej dodaje ograniczoną abstrakcję, napisałbym funkcję konwersji / makro, aby uzyskać lepszą abstrakcję
wirrbel
1
czy rzutowanie bardzo dużego podwójnego na int nie ryzykowałoby typowych problemów z różnicą zaokrąglenia liczb zmiennoprzecinkowych?
Philipp
przy typach liczb całkowitych normalnej precyzji nie powinno to stanowić problemu, o ile do konwersji używasz zmiennoprzecinkowego podwójnej precyzji. masz rację, gdy używasz wartości long longzakresu.
wirrbel
145

Zamiast tego utwórz NANOSECONDS_IN_ONE_SECOND jako to, co reprezentuje.

Lub krótsza, lepsza nazwa, jeśli możesz o niej pomyśleć.

JohnB
źródło
58
Powiedziałbym, Nanoseconds_Per_Secondale jest to, moim zdaniem, poprawna odpowiedź.
KChaloux
8
@Mathew Nie rozumiem o co ci chodzi. Nie ma nic złego w mówieniu milimetrów na metr. Być może sugerujesz, że jest on zbędny w tym nanosekundie OZNACZA miliard ułamków sekundy, ale nie ma nic złego w stwierdzeniu go ponownie. To tak, jakby powiedzieć 1 + 1 = 2. „x na rok” ma większy sens, gdy xiy są rozłączne, na przykład „jednostki na pół tuzina” lub „nanosekundy na milisekundę”
Mark Canlas
7
@MathewFoscarini Właściwie nie, w tym kontekście tak nie jest. Gdyby tak było, stała o nazwie NANOSECONDSnie ma znaczenia, ponieważ nie można powiedzieć, do czego ma się odnosić. Podobnie NANOSECONDS_PER_MICROSECONDjest podobna poprawna stała, która ma sens.
Izkata
5
@MathewFoscarini, „milimetry na metr” to sposób na usunięcie jednostki konwersji, aby uzyskać surową wartość. 1mm/1m = 1000, co jest dokładnie tym, co się tutaj robi.
zzzzBov
11
Po co tyle pisania? NS_PER_SECpowinno być oczywiste dla każdego, kto powinien mieć do czynienia z nanosekundami.
Rex Kerr
67

Stałe mają oznaczać liczby. Nie ma żadnego dodatkowego znaczenia w ONE_BILLIONcelu 1000000000. W rzeczywistości sprawia, że ​​jest to bardziej mylące, ponieważ w różnych językach naturalnych miliard oznacza coś innego (tysiąc milionów lub milion milionów)! Jeśli chcesz napisać to krócej, istnieje duże prawdopodobieństwo, że Twój język programowania pozwala na stosowanie notacji naukowej, tj 1e9. W przeciwnym razie zgadzam się z @JohnB, że ta liczba naprawdę oznacza liczbę nanosekund na sekundę, więc nazwij to.

Thijs van Dien
źródło
9
Dobrze, że miliardy w różnych językach oznaczają różną liczbę zer.
frozenkoi
3
sugeruje zmianę zwykłych języków na języki naturalne. regularne oznacza coś innego ...
jk.
Różne interpretacje „miliarda” w językach to taki dobry punkt! Dlaczego nie mogę dwukrotnie głosować na twoją odpowiedź!
DSF
3
Nie potrzebujesz różnych języków. Nie potrzebujesz nawet różnych krajów. W brytyjskim angielskim „miliard” oznacza coś innego przed i po 1974 r. W oficjalnej komunikacji (środki masowego przekazu i rząd), i oba te zastosowania nadal istnieją.
Jörg W Mittag
1
ONE_BILLION nie ma żadnego dodatkowego znaczenia na 10000000000. Nie zgadzam się. (Wskazówka: Celowo źle wprowadziłem w błąd i dodałem kolejne zero; zauważyłbym, gdybym o tym nie wspomniał?)
Keith Thompson
27

W przypadku jednego lub dwóch zastosowań użyłbym konwencji:

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

Jest to całkowicie zrozumiałe, kompiluje się na stałe i ciężko jest popsuć.

Jest to również bardzo przydatne w przypadkach takich jak:

var Time = 24 * 60 * 60;

gdzie łatwo zauważyć, że mówimy o jednym dniu w ciągu kilku sekund.

Sklivvz
źródło
Tak zwykle robię. Ma również tę zaletę, że nie zapomnę, że wczoraj zdefiniowałem NANOSECONDS_IN_ONE_SECOND i dzisiaj zdefiniowałem NANOSECONDS_PER_SECOND. I może jutro ONE_AMERICAN_BILLION.
Thomas Padron-McCarthy
Z pewnością „SecondsInOneDay = 24 * 60 * 60” jest jeszcze łatwiejszy?
JBRWilkinson
@JBRWilkinson, oczywiście, mój początkowy fragment używał klasy instance.Time = ..., ale potem go
stępiłem
3
W C lub C ++ (1000 * 1000 * 1000)jest typu int, który musi mieć tylko 16 bitów, więc może się przepełnić. Możesz pisać, (1000L * 1000L * 1000L)aby tego uniknąć.
Keith Thompson,
Często to robię. Pracuje bardzo dobrze.
vy32
10

Długość wartości nie jest tym, co określa, czy stała jest potrzebna, czy nie.

Używasz stałych, aby unikać magicznych liczb , a nie pisać.

Na przykład są to doskonale poprawne stałe:

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;

Posługiwać się:

public static final int NANOSECS_PER_SECOND = 1000000000;

(próbki kodu są w Javie, przetłumacz na swój ulubiony język)

Tulains Córdova
źródło
3
+1 Nazwane liczby są prawie bezużyteczne. Celem stałych jest nadanie znaczenia tym liczbom. Co oni reprezentują? Co oni liczą, ograniczają lub formalnie nazywają współczynnik? Nie jaka jest wartość liczby.
JustinC
5
Widząc, jak mówimy o Javie, spostrzeżę, że od Java 7 możemy wstawiać podkreślenia w liczbach, aby poprawić czytelność!
Nick
2
To okropne przykłady poprawnych stałych. Powinny być wyliczeniami, z wyjątkiem tego, że zostały stworzone przed wyliczeniami.
Christoffer Hammarström
@ ChristofferHammarström Rzeczywiście zostały utworzone przed wyliczeniami, są częścią klasy ResultSet w pakiecie SQL Java SDK.
Tulains Córdova
2
@ ChristofferHammarström Są złe, ponieważ teraz mamy wyliczenia, ale nie za to, że są bez znaczenia. Wyliczenie nie istniało, kiedy te klasy zostały utworzone, a w celu rozróżnienia między wzajemnie wykluczającymi się opcjami, takimi jak FETCH_FORWARD i FETCH_REVERSE, nadaje im inną wartość. Wartość nie ma znaczenia, tylko fakt, że są różne.
Tulains Córdova
8

Amerykański czy europejski miliard?

(lub pod względem technicznym miliard w krótkiej lub długiej skali - jeden to 1000 milionów, drugi to milion milionów).

Biorąc pod uwagę to zamieszanie, powiedziałbym, że tak - sensowne jest zdefiniowanie go raz i pozostanie z nim, podobnie dotyczy każdej stałej, na którą trzeba uzgodnić definicję - zdefiniować raz.

gbjbaanb
źródło
17
„Amerykański czy europejski miliard?” - „Co? Nie wiem tego! Ahhhhh !!!!”
Tesserex
Przynajmniej w Wielkiej Brytanii od dawna przyjęliśmy 1e9 miliardów.
Jack Aidley,
1
@Tesserex - wiesz, musisz wiedzieć o tych rzeczach, kiedy jesteś królem.
gbjbaanb
5

Powody, dla których nie

Po pierwsze, oto powód, dla którego nie należy pisać podkreślników ani używać żadnej sztuczki, aby to zasymulować: utrudnia to znalezienie stałych w kodzie. Załóżmy, że jakiś program wykazuje gdzieś w działaniu zakodowaną wartość 1500000 dla jakiegoś parametru. Chcę wiedzieć, gdzie w kodzie źródłowym programu tak się dzieje, więc szukam kodu 1500000i nic nie znajduję. Czemu? Może być w systemie szesnastkowym (ale dlaczego tak okrągłej liczby dziesiętnej). Bez mojej wiedzy stała jest tak naprawdę zapisana jako 1_500_000. Potrzebowałem wyrażenia regularnego 1_?500_?000.

Prowadzenie postaci w komentarzu

To, że jeden rodzaj pomocy wizualnej nie jest dostępny lub nie chcemy jej używać z powyższego powodu, nie oznacza, że ​​nie możemy skorzystać z dwóch wymiarów pliku tekstowego, aby stworzyć alternatywną pomoc wizualną:

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

Dzięki temu możemy łatwo przekonać się, że istnieją trzy grupy trzech zer. Jednak nadal możemy grepować kod źródłowy 1000000000i go znaleźć.

Kolorowanie składni

Edytor tekstu z programowalnym kolorowaniem składni może być utworzony w celu grupowania cyfr cyfr w stałych liczbowych naprzemiennymi kolorami dla lepszej czytelności. Nie musimy nic robić w kodzie.

Przetwarzanie wstępne: C, C ++, Cel C

Teraz, jeśli naprawdę chcemy przecinków między cyframi, w C i C ++ możemy użyć przetwarzania wstępnego:

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

Działa dla liczb takich jak TH(1,234,567,890).

Makro podobne do TH może również działać z wklejaniem tokenów, a nie z arytmetyką. W preprocesorze C ##operator binarny („wklej token”) może być użyty w ciele makra w celu wklejenia dwóch operandów w jeden token. Jeden lub oba operandy mogą być argumentami makr. Minusem tutaj (stwarzającym dla nas ryzyko) jest to, że jeśli wynikowa catenacja nie jest prawidłowym tokenem, zachowanie jest niezdefiniowane.

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

Teraz

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

Istnieją programy typu C, które wklejają identyfikatory i używają wyników do nazwania zmiennych globalnych i funkcji, i są okropne w pracy, ponieważ są odporne na narzędzia takie jak narzędzia GNU i ctag.

Kaz
źródło
2
+1 za jedno z najlepszych nadużyć preprocesora, jakie widziałem. Nadal jednak wybrałbym NSEC_PER_SEC lub coś w produkcji.
Victor
Bardzo prawie -1 za nadużywanie preprocesora :)
CVn
3

Tak, to brzmi jak rozsądny pomysł. Błędy DIGIT off-by-one są jeszcze gorsze niż niesławne błędy off-by-one. Chociaż może to powodować zamieszanie dla innych osób (w tym twojego przyszłego ja) w czytaniu kodu.

Bardziej objaśniająca nazwa, taka jak NANOSEC_PER_SEC, wydaje się dobra, ponieważ zwiększyłaby przejrzystość w przypadku użycia jej na czas. Jednak nie ma sensu używać go w kontekstach innych niż czas i byłoby niepraktyczne tworzyć osobne 1 000 000 000 dla każdej sytuacji.

To, co naprawdę chcesz zrobić, na pierwszy rzut oka głupie, to „podzielić na sekundę”. Pozostawia to NANO_PER, który jest nie tylko niezależny od języka (10 ^ 9 w Ameryce i Europie), ale także niezależny od sytuacji (bez ograniczeń jednostek), i jest łatwy do pisania i czytania.

MegaWidget
źródło
ten post jest raczej trudny do odczytania (ściana tekstu). Czy mógłbyś edytować go w lepszym kształcie?
komar
3

Ogólnie rzecz biorąc, złym pomysłem jest używanie stałych skalarnych do konwersji jednostek, a jeśli odkryjesz, że tworzysz stałe dla takich rzeczy, robisz konwersję w zbyt wielu miejscach.

Gdy masz ilość jednej jednostki (powiedzmy 10 sekund) i chcesz przekonwertować na inną jednostkę (np. Nanosekundy); to jest właśnie czas na użycie systemu pisma w twoim języku, aby upewnić się, że jednostki są skalowane tak, jak chcesz.

Dokonać funkcja ma Nanosecondsparametr i zapewnić operatorom konwersji i / lub konstruktorów w tej klasie Seconds, Minuteslub co-ma-ty. To gdzie się const intlub #defineczy 1e9widać w innych odpowiedzi należy.

Pozwala to uniknąć przemieszczania się zmiennych o niejednoznacznych jednostkach wokół kodu; i zapobiega całemu zestawowi błędów, od których zastosowano niewłaściwe mnożenie / dzielenie, lub zostało już zastosowane, lub ilość faktycznie była odległością zamiast czasu, lub ...

Ponadto w takich klasach dobrze jest tworzyć konstrukcje ze zwykłego scalarsprivate i używać statycznego „MakeSeconds (int)” lub podobnego, aby zniechęcić do niechlujnego używania nieprzezroczystych liczb.

Dokładniej w twoim przykładzie, w C ++ sprawdź Boost.Chrono .

wartość r
źródło
1
+ Przynajmniej użyj typowego typu ze współczynnikiem skalowania lub przesunięcia w stosunku do podstawy, podobnie jak często złośliwa strefa czasowa.
JustinC
1

Osobiście nie uważałbym za dobrą praktykę tworzenia stałej, chyba że musi to być stała. Jeśli będzie w wielu miejscach, a zdefiniowanie go w górnej części pliku do modyfikacji / lub testowania będzie przydatne, to absolutnie przydatne.

Jeśli to tylko dlatego, że trudno jest pisać? w takim razie nie.

Osobiście, jeśli mam kod innej osoby, który ma stałą zdefiniowaną, ogólnie uważam to za ważny aspekt kodu. Np. Tcp utrzymuje przy życiu liczniki, maksymalna dozwolona liczba połączeń. Gdybym musiał go debugować, prawdopodobnie poświęciłbym temu wiele niepotrzebnej uwagi, próbując dowiedzieć się, dlaczego / gdzie jest używany.

Simon McLoughlin
źródło
Rozumiem, ale gdyby programiści banku musieli wprowadzić stałą dla każdej liczby, którą można przenieść, oprogramowanie byłoby gigantyczne, niemożliwe do zarządzania i wolne. Mogłem sobie tylko wyobrazić, jak by to było, wyobraź sobie, że powiedzenie, że przelewanie pieniędzy zajmie 3 dni robocze ... O mój Boże, to jest !!!
Simon McLoughlin
Mój bank zajmuje 3 dni, aby przelać pieniądze :(
Reactgular
1
@MathewFoscarini bankierzy używają Excela, nie potrzebują programistów;)
Mateusz
@ Simon W zależności od języka i kompilatora, stałe powinny zostać zoptymalizowane w kodzie, powodując niewielki narzut. Rozumiem twój punkt widzenia, ale stałych można używać wszędzie tam, gdzie użycie nazwy zamiast magicznej liczby pomogłoby w odczytaniu kodu.
Steven
Niezręczne czytanie to o wiele bardziej problem niż pisanie.
Alb
0

Kiedy pomyślisz o tym, dlaczego w tytule pytania napisałeś „1 miliard” zamiast „1000000000”, zrozumiesz, dlaczego odpowiedź brzmi „tak”.

Alba
źródło
0

Nie twórz stałej dla swoich wielkich literałów. Potrzebowałbyś stałej dla każdego takiego dosłowności, co jest (moim zdaniem) kompletnym żartem. Jeśli desperacko potrzebujesz uczynić literały bardziej zrozumiałymi bez pomocy takich rzeczy jak podświetlanie składni, możesz (choć nie chciałbym) tworzyć funkcji lub makr, aby uczynić twoje życie „łatwiejszym”:

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

int largeNumber1 = SPLIT3(123,456,789);
int largeNumber2 = 123456789;
Thomas Eding
źródło
0

Zrobiłbym to:

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

lub

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

Jeśli chodzi o liczbę nanosekund na sekundę: nano jest „odwrotnością” giga.

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

Zwróć uwagę na „Sci” - w przypadku nauki, podobnie jak w komputerach, znaczenia kilo, mega, giga itp. Są różne: 1024 (2 ^ 10), 1024 * 1024 (2 ^ 20) itd. 2 megabajty to nie 2 000 000 bajtów .

UPDATE Komentator zwrócił uwagę, że istnieją specjalne warunki dla cyfrowych wykładników 2: http://en.wikipedia.org/wiki/Mebibyte

Pan TA
źródło
„2 megabajty to nie 2 000 000 bajtów”. Zapytaj dowolnego producenta twardego dysku obrotowego. (Nie downvoter, btw.)
CVn
@ michaelkjorling to pytanie programistyczne, a nie etyka biznesowa czy marketingowa. Zgadzam się co do dysków twardych, ale to inny temat. I ouch o mniejszych głosach!
Pan TA
1
W rzeczywistości 2 megabajty to 2 000 000 bajtów. 2 Mebibytes ma 2097152 bajtów. Zobacz en.wikipedia.org/wiki/Mebibyte
vy32
@ vy32 dzięki, nigdy wcześniej o tym nie słyszałem. Zaktualizuję moją odpowiedź, aby to odzwierciedlić.
Pan TA,
@ Mr.TA, nie ma problemu! Ciężko pracujemy nad dostosowaniem informatyki do jednostek SI! Dołączyć do klubu.
vy32