Мови, які не дозволяють підкреслити цілі константи, чи є хорошою практикою створення константи на 1 мільярд?


39

Мови, які не дозволяють підкреслити цілі літерали , чи гарна ідея створити константу на 1 мільярд? наприклад, в C ++:

size_t ONE_BILLION = 1000000000;

Звичайно, ми не повинні створювати константи для малих чисел, таких як 100. Але з 9 нулями, можливо, просто залишити нуль або додати додатковий у такому коді:

tv_sec = timeInNanosec / 1000000000;
tv_nsec = timeInNanosec % 1000000000;

24
Я сподіваюся , що все тут голосують за NO . Таким чином, можливо, одного дня мій банк перерахує мільярд доларів на мій рахунок, тому що програміст не використовував константу і неправильно поставив нуль! :)
Реакційний

43
Чому б не створити константи для невеликих чисел? Що означає 100? Якщо немає якогось контексту, це магічне число.
Аллан

4
@MathewFoscarini Взагалі помилки можуть йти в будь-який бік. Але коли справа стосується вашого банку, помилки завжди будуть йти проти вас.
Еморі

23
Подумайте про написання 1e9, 10^9або 1_000_000_000якщо мова, яку ви використовуєте, підтримує це.
хаммар

Відповіді:


33

Більшість мов мають якесь експоненціальне позначення. Мільйон - це 1e6(означає 1 раз 10 на потужність 6). Це в основному вирішує питання навіть краще, ніж більшість пропозицій тут.

У багатьох мовах, схожих на С, однак наукове позначення визначає тип плаваючої крапки , що шкода, якщо вам справді потрібен інт. Однак ви можете легко набрати цю константу, щоб уникнути неявних перетворень у формулі.

n / int(1e9) ділиться на мільярд.

У вашому прикладі, маючи справу з фізичними величинами (час у наносекунд), я взагалі запитаю себе, чи є ціле число правильним типом. Насправді плаваюча точка doubleможе бути краще підходить при роботі з вимірюваними кількостями (хоча, звичайно, є випадки, коли ви вважаєте за краще б long long).


6
Я думаю, що рішення NANOSECONDS_IN_ONE_SECOND набагато чіткіше та акуратніше
Томас Боніні

1
Питання стосувалося цілих лібералів, і я пропоную використовувати наукові позначення. Чи потрібно це робити на місці чи визначаючи константу - це питання структуризації коду, про який не було запитано. Визначаючи константу додає обмежену абстракцію, я б написав функцію перетворення / макрос для досягнення кращої абстракції
wirrbel

1
кинув би дуже великий подвійний на int, не ризикуючи типовими проблемами різниці округлення чисел з плаваючою комою?
Філіп

з типовими цілими числами звичайної точності це не повинно бути проблемою, якщо ви використовуєте поплавок подвійної точності для перетворення з. ви праві, коли використовуєте значення long longдіапазону.
wirrbel

145

Створіть замість нього NANOSECONDS_IN_ONE_SECOND як те, що він представляє.

Або коротше, краще ім'я, якщо ви можете придумати його.


58
Я б сказав, Nanoseconds_Per_Secondале це, на мій погляд, правильна відповідь.
KChaloux

8
@Mathew Я не розумію тебе. Немає нічого поганого в тому, щоб говорити міліметри на метр. Ви можете мати на увазі, що це зайве, оскільки наносекунда означає один мільярд частки секунди, але немає нічого поганого в тому, щоб повторити це. Це як би сказати 1 + 1 = 2. "x per y" продовжує мати більше сенсу, коли x і y нерозривні, як "одиниці на півдесятка" або "наносекунд на мілісекунд"
Марк Канлас

7
@MathewFoscarini Насправді ні, в цьому контексті це не так. Якщо б це було, то постійна назва не NANOSECONDSмає сенсу, оскільки ви не можете сказати, до чого вона повинна застосовуватися. Так само NANOSECONDS_PER_MICROSECONDє аналогічною дійсною константою, яка має сенс.
Ізката

5
@MathewFoscarini, "міліметр на метр" - це спосіб вилучити одиницю при перетворенні, щоб отримати вихідне значення. 1mm/1m = 1000, що саме і полягає в тому, що тут робиться.
zzzzBov

11
Чому так багато набирати текст? NS_PER_SECмає бути очевидним для кожного, хто повинен мати справу з наносекундами.
Рекс Керр

67

Константи мають на меті давати значення числам. Існує не будь-який додатковий сенс в ONE_BILLIONдо 1000000000. Власне, це робить його більш заплутаним, адже в різних природних мовах мільярд означає щось інше (або тисяча мільйонів, або мільйон мільйонів)! Якщо ви хочете написати це коротше, є хороший шанс, що ваша мова програмування дозволяє використовувати наукові позначення, тобто 1e9. В іншому випадку я погоджуюся з @JohnB, що це число справді означає кількість наносекунд за секунду, тому назвіть це так.


9
Добре вказуючи, що мільярд різних мов означає різну кількість нулів.
заморожений

3
запропонував би змінити звичайні мови на природні. регулярний означає щось інше ...
jk.

Різні інтерпретації "мільярду" на мовах - це хороший момент! Чому я не можу подякувати вашу відповідь двічі!
ДСФ

3
Вам не потрібні різні мови. Вам навіть не потрібні різні країни. У англійській англійській мові "мільярд" означає щось інше до і після 1974 року в офіційних комунікаціях (ЗМІ та уряді), і обидва звичаї все ще існують.
Йорг W Міттаг

1
" Немає жодного додаткового значення в ONE_BILLION до 10000000000. Я не погоджуюся. (Підказка: я навмисно неправильно написав вас і додав ще один нуль; помітив би, якби я не згадував про це?"
Кіт Томпсон

27

Для однієї або двох звичаїв я використовую конвенцію:

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

Це ідеально зрозуміло, збирається до постійної і важко викрутити.

Також це дуже корисно у таких випадках, як:

var Time = 24 * 60 * 60;

там, де це легко помітити, ми говоримо про один день за секунди.


Це те, що я зазвичай роблю. Він також має перевагу в тому, що я не забуду, що я вчора визначив NANOSECONDS_IN_ONE_SECOND і визначив NANOSECONDS_PER_SECOND сьогодні. І, можливо, завтра ONE_AMERICAN_BILLION
Томас Падрон-Маккарті

Звичайно, "SecondsInOneDay = 24 * 60 * 60" все ж простіше?
JBRWilkinson

@JBRWilkinson впевнений, що в моєму початковому фрагменті використовувався клас instance.Time = ..., але потім я його скинув ...
Sklivvz

3
В C або C ++ (1000 * 1000 * 1000)має тип int, для якого потрібно лише 16 біт, щоб він міг переповнюватися. Ви можете написати, (1000L * 1000L * 1000L)щоб уникнути цього.
Кіт Томпсон

Я цим багато займаюся. Це працює дуже добре.
vy32

10

Довжина значення не є тим, що визначає, потрібна константа чи ні.

Ви використовуєте константи, щоб уникнути магічних чисел , а не уникати набору тексту.

Наприклад, це абсолютно дійсні константи:

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;

Використання:

public static final int NANOSECS_PER_SECOND = 1000000000;

(зразки коду є на Java, перекладіть на улюблену мову)


3
+1 Названі номери майже марні. Мета констант - надати значення цим числам. Що вони представляють? Що вони рахують чи обмежують або офіційно називають коефіцієнтом? Не те, яка цінність підрахунку.
JustinC


2
Це жахливі приклади дійсних констант. Вони повинні були бути перерахунками, за винятком того, що вони були створені раніше.
Крістофер Хаммарстрем

@ ChristofferHammarström Вони справді були створені перед перерахунками, вони входять до класу ResultSet в пакеті SQL Java SDK.
Тулен Кордова

2
@ ChristofferHammarström Вони погані, тому що зараз у нас є переслідування, але не за те, що вони не мають значення. Enum не існував, коли були створені ці класи, і щоб розмежувати взаємовиключні варіанти, такі як FETCH_FORWARD і FETCH_REVERSE, надає їм іншого значення. Значення не має значення, лише те, що вони різні.
Тулен Кордова

8

Американський чи європейський мільярд?

(або в технічному плані - мільярд у короткому або довгому масштабі - один - 1000 мільйонів, інший - мільйон мільйонів).

Враховуючи цю плутанину, тоді я б сказав так - має сенс визначити її один раз і продовжувати її, так само це стосується будь-якої постійної, для якої потрібно узгодити визначення - визначте його один раз.


17
"Американський чи європейський мільярд?" - "Що? Я цього не знаю! Аааааааааа!"
Тессерекс

Принаймні, у Великобританії ми давно прийняли 1,9 мільярда.
Джек Едлі

1
@Tesserex - ну, ти повинен знати ці речі, коли ти король, знаєш.
gbjbaanb

5

Причини не робити

По-перше, тут є причина не писати підкреслення або використовувати якийсь трюк для їх моделювання: це константи ускладнює пошук у коді. Припустимо, що деяка програма демонструє десь під час своєї роботи жорстке кодування значення 1500000 для якогось параметра. Я хочу знати, де в вихідному коді програми це відбувається насправді, тому я перебираю код 1500000і нічого не знаходжу. Чому? Це може бути в шістнадцятковій цифрі (але навіщо таке кругле десяткове число). Невідома для мене, константа насправді пишеться як 1_500_000. Мені потрібен був регулярний вираз 1_?500_?000.

Керівні персонажі в коментарі

Тільки тому, що один вид наочної допомоги не доступний, або ми не хочемо використовувати його з вищевказаної причини, не означає, що ми не можемо скористатися двома вимірами текстового файлу для створення альтернативної візуальної допомоги:

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

З цим ми легко можемо переконати себе у тому, що існує три групи з трьох нулів. Тим не менш, ми все ще можемо зібрати вихідний код 1000000000і знайти його.

Розмальовки синтаксису

Текстовий редактор з програмованим синтаксичним забарвленням можна зробити для кольорових груп цифрами в числових константах з чергуванням кольорів для кращої читабельності. Нам не потрібно нічого робити в коді.

Попередня обробка: C, C ++, ціль C

Тепер, якщо ми дійсно хочемо деякої коми після цифр, в C і C ++ ми можемо використовувати деяку попередню обробку:

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

Працює для таких чисел TH(1,234,567,890).

Макрос, схожий на TH, також може працювати з вставкою лексеми, а не арифметикою. У препроцесорі С двійковий ##оператор ("лексема вставки") може використовуватися в макроконтролі, щоб склеїти два операнди в один маркер. Один або обидва операнди можуть бути макроаргументами. Мінус тут (створюючи для нас ризик) полягає в тому, що якщо результуюча катенація не є дійсним маркером, поведінка не визначається.

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

Тепер

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

Програми C, які склеюють ідентифікатори та використовують результати для іменування глобальних змінних та функцій, існують і з ними жахливо працювати, оскільки вони непроникні для таких інструментів, як id-утиліти та ctag GNU.


2
+1 за одне з найкращих зловживань препроцесора, який я бачив. Я б все-таки поїхав з NSEC_PER_SEC або щось у виробництві.
Віктор

Дуже майже -1 за зловживання препроцесором :)
CVn

3

Так, це звучить як розумна ідея. Помилки DIGIT від одного за одним навіть гірші, ніж сумнозвісні помилки один за одним. Хоча це може створити плутанину у інших людей (у тому числі у вашої майбутньої особи) для читання коду.

Більш роз’яснювальна назва, як NANOSEC_PER_SEC, здається гарною, оскільки вона додасть ясності там, де вона використовується протягом часу. Однак використовувати його в інших контекстах, окрім часу, немає сенсу, і було б недоцільно створювати окремі 1 000 000 000 для кожної ситуації.

Те, що ви насправді хочете зробити, нерозумно, як здається спочатку, - це «розділити на секунду». Це залишає NANO_PER, який не тільки не залежить від мови (10 ^ 9 в Америці та Європі), але й незалежний від ситуації (не обмежуючи одиниці), і його легко вводити та читати.


цю публікацію досить важко читати (стіна тексту). Ви б не хотіли відредагувати його на кращу форму?
гнат

3

Загалом, погана ідея використовувати скалярні константи для перетворення одиниць, і якщо ви вважаєте, що робите константи для таких речей, ви робите перетворення в занадто багато місця.

Коли ви маєте кількість однієї одиниці (скажімо, 10 секунд) і хочете перетворити її на іншу одиницю (тобто наносекунди); це саме час використовувати систему типів вашої мови, щоб переконатися, що одиниці дійсно масштабуються так, як ви плануєте.

Зробіть свою функцію взяти Nanosecondsпараметр, і забезпечують оператори перетворення і / або конструктор в цьому класі Seconds, Minutesабо що-у-ви. Це де ваш const intабо #defineабо 1e9бачили в інших відповідях належить.

Це дозволяє уникати змінних неоднозначних одиниць, що плавають навколо вашого коду; і запобігає цілому ряду помилок, звідки було застосовано неправильне множення / ділення, або воно вже було застосовано, або кількість була фактично відстань замість часу, або ...

Крім того, в таких класах добре виготовити конструкцію з простого скаляраприватним і використовувати статичний "MakeSeconds (int)" або подібний, щоб відмовити від неохайного використання непрозорих чисел.

Більш конкретно до вашого прикладу, в C ++ перевірте Boost.Chrono .


1
+ Принаймні, використовуйте загальний тип з коефіцієнтом масштабування або зміщення від бази, як і часто злісний часовий пояс.
JustinC

1

Я особисто не вважав би доброю практикою створювати константу, якщо вона не повинна бути постійною. Якщо його буде в декількох місцях і визначено його у верхній частині файлу для модифікації / або тестування, буде корисно, то абсолютно.

Якщо це просто тому, що його незручно набрати? то ні.

Особисто, якщо я отримав чийсь код, який постійно визначався, я, як правило, вважаю це важливим аспектом коду. Напр. Tcp підтримують живі таймери, максимально допустима кількість з'єднань. Якби мені довелося це налагоджувати, я, мабуть, приділяв би йому багато зайвої уваги, намагаючись з’ясувати, чому / де його використовують.


Я жартую, але якби банківські програмісти повинні були робити константи для кожного номера, ви могли передати програмне забезпечення, було б гігантським, некерованим та повільним. Я міг би лише уявити, що це буде, уявіть, що мені сказали, що потрібно три робочі дні, щоб перевести гроші на .... О, БОЖИЙ БОГ, ТОЙ !!!
Simon McLoughlin

Моєму банку
потрібні

1
@MathewFoscarini банкіри використовують Excel, вони не потребують програмістів;)
Матеуш

@Simon Залежно від мови та компілятора, константи повинні бути оптимізовані до коду, маючи невеликі накладні витрати. Я розумію вашу думку, але константи можна використовувати там, де використання імені замість магічного числа допоможе читати код.
Стівен

Незручно читати - це набагато більше питання, ніж незручне для набору.
Альб

0

Коли ви подумаєте, чому ви написали "1 мільярд" замість "1000000000" у назві запитання, ви зрозумієте, чому відповідь "так".


0

Не створюйте константу для своїх великих літералів. Вам знадобиться константа для кожного такого буквального, що є (на мою думку) повним жартом. Якщо вам відчайдушно потрібно зробити ваші літерали зрозумілішими без допомоги таких речей, як підсвічування синтаксису, ви можете (хоча я цього не хотів) створити функції або макроси, щоб полегшити ваше життя:

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

int largeNumber1 = SPLIT3(123,456,789);
int largeNumber2 = 123456789;

0

Я би зробив це:

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

або

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

Щодо кількості наносекунд в секунду: нано - "обернена" гіга.

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

Зверніть увагу на "Sci" - для наукових, як і в комп'ютерах, значення кіло, мега, гіга тощо відрізняються: 1024 (2 ^ 10), 1024 * 1024 (2 ^ 20) і т. Д. 2 мегабайти - це не 2 000 000 байт .

UPDATE Commenter зазначив, що існують спеціальні терміни для цифрових експонентів 2: http://en.wikipedia.org/wiki/Mebibyte


"2 мегабайти - це не 2 000 000 байт." Запитайте будь-якого виробника жорсткого диска на прядильній платформі. (Не потік, btw.)
CVn

@michaelkjorling - це питання програмування, а не ділова етика чи маркетинг. Я згоден з приводу жорстких дисків, але це інша тема. І про голосування проти!
Містер ТА

1
Насправді 2 мегабайти - це 2 000 000 байт. 2 Мебібайти - 2 097 152 байт. Дивіться en.wikipedia.org/wiki/Mebibyte
vy32

@ vy32 дякую, ніколи про це не чув. Буду оновити свою відповідь, щоб це відобразити.
Містер ТА

@ Mr.TA, жодних проблем! Ми наполегливо працюємо над приведенням інформатики у відповідність до підрозділів SI! Приєднатися до клубу.
vy32
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.