На языках, которые не допускают подчеркивания в целочисленных константах, является ли хорошей практикой создание константы для 1 миллиарда?


39

На языках, которые не допускают подчеркивания в целочисленных литералах , это хорошая идея, чтобы создать константу за 1 миллиард? например, в C ++:

size_t ONE_BILLION = 1000000000;

Конечно, мы не должны создавать константы для небольших чисел, таких как 100. Но с 9 нулями, возможно, легко обнулить ноль или добавить дополнительный в коде так:

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

24
Я надеюсь, что все здесь голосуют за НЕТ . Таким образом, возможно, однажды мой банк переведет один миллиард долларов на мой счет, потому что программист не использовал константу и потерял ноль! :)
Reactgular

43
Почему бы не создать константы для небольших чисел? Что значит 100? Если нет некоторого контекста, это магическое число.
Аллан

4
@MathewFoscarini В общем, ошибки могут идти в любом направлении. Но когда дело доходит до вашего банка, ошибки всегда будут идти против вас.
Эмори

23
Подумайте о написании 1e9, 10^9или 1_000_000_000если язык, который вы используете, поддерживает это.
Хаммар

Ответы:


33

Большинство языков имеют своего рода экспоненциальные обозначения. Миллион есть 1e6(имеется в виду 1 раз 10 к степени 6). Это в основном решает проблему даже лучше, чем большинство предложений здесь.

Однако во многих C-подобных языках научная нотация определяет тип с плавающей запятой , что не очень хорошо, если вам действительно нужен int. Однако вы можете легко привести эту константу к типу, чтобы избежать неявных преобразований в формуле.

n / int(1e9) разделил бы на миллиард.

В вашем примере, касающемся физических величин (время в наносекунде), я обычно спрашиваю себя, является ли целое число правильным типом. Фактически, плавающая точка doubleможет быть лучше приспособлена для работы с измеримыми величинами (хотя, конечно, есть случаи, когда вы бы предпочли a long long).


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

1
Вопрос был о целочисленных либералах, и я предлагаю использовать научную нотацию. Делать это на месте или определять константу - это вопрос структурирования кода, который не был задан в вопросе. Определение константы добавляет ограниченную абстракцию, я написал бы функцию преобразования / макрос для достижения лучшей абстракции
wirrbel

1
будет ли приведение очень большого двойного к целому числу не рисковать типичными проблемами разности округления чисел с плавающей запятой?
Филипп

с целочисленными типами с нормальной точностью это не должно быть проблемой, если вы используете для вычисления число с плавающей запятой двойной точности. Вы правы при использовании значений long longдиапазона.
wirrbel

145

Создайте один с именем NANOSECONDS_IN_ONE_SECOND вместо того, что он представляет.

Или более короткое, лучшее имя, если вы можете придумать одно.


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

8
@ Мэтью, я не понимаю твою точку зрения. Нет ничего плохого в том, чтобы говорить миллиметры на метр. Возможно, вы намекаете на то, что это избыточно, в эту наносекунду ОЗНАЧАЕТ один миллиард долей секунды, но нет ничего плохого в том, чтобы заявить это снова. Это все равно, что сказать 1 + 1 = 2. «x за 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
Хорошо, что миллиард в разных языках означает разное количество нулей.
Frozenkoi

3
предложил бы заменить обычные языки на естественные языки. регулярный означает что-то еще ...
JK.

Разные толкования "миллиарда" в языках такая хорошая точка! Почему я не могу подтвердить ваш ответ дважды!
DSF

3
Вам не нужны разные языки. Вам даже не нужны разные страны. В британском английском «миллиард» означает нечто иное до и после 1974 года в официальных сообщениях (средства массовой информации и правительство), и оба эти способа все еще существуют.
Йорг Миттаг

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: конечно, мой первоначальный фрагмент кода использовал класс instance.Time = ..., но потом я его
ошарашил

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
"Американский или европейский миллиард?" - "Что? Я не знаю этого! Аааааааа !!!!"
Tesserex

По крайней мере, в Великобритании мы давно приняли 1,9 миллиарда долларов.
Джек Эйдли,

1
@ Тессерекс - ну, ты должен знать эти вещи, когда ты король, ты знаешь.
gbjbaanb

5

Причины не

Во-первых, вот причина, по которой вы не пишете подчеркивания или не используете какой-либо трюк для его симуляции: это затрудняет поиск констант в коде. Предположим, что какая-то программа показывает где-то в своей работе жестко закодированное значение 1500000 для некоторого параметра. Я хочу знать, где в исходном коде программы это на самом деле происходит, поэтому я набираю код 1500000и ничего не нахожу. Зачем? Это может быть в шестнадцатеричном (но почему для такого круглого десятичного числа). Без моего ведома, константа на самом деле записывается как 1_500_000. Мне нужно регулярное выражение 1_?500_?000.

Руководящие символы в комментарии

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

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

При этом мы можем легко убедить себя, что есть три группы из трех нулей. Тем не менее, мы все еще можем 1000000000найти исходный код и найти его.

Синтаксис Раскраска

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

Предварительная обработка: C, C ++, Objective 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, также может работать с вставкой токена, а не с арифметикой. В препроцессоре C бинарный ##оператор («вставка токена») может использоваться в теле макроса для вставки двух операндов в один токен. Один или оба операнда могут быть макро-аргументами. Недостатком (создающим для нас риск) является то, что если полученная цепочка не является допустимым токеном, поведение не определено.

#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, которые вставляют идентификаторы и используют результаты для именования глобальных переменных и функций, существуют и с ними ужасно работать, потому что они невосприимчивы к таким инструментам, как GNU id-utils и ctags.


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 поддерживает таймеры, максимально допустимое количество соединений. Если бы мне пришлось отлаживать его, я бы, вероятно, уделил ему слишком много внимания, пытаясь выяснить, почему / где он используется.


Я получаю шутку, но если бы банковские программисты должны были делать константу для каждого числа, которое вы могли бы передать, программное обеспечение было бы гигантским, неуправляемым и медленным. Я мог только вообразить, на что это будет похоже, представьте, что мне сказали бы, что потребуется 3 рабочих дня, чтобы перевести деньги ... О, Боже мой, вот так !!!
Саймон Маклафлин

Мой банк переводит деньги за 3 дня :(
Reactgular

1
@MathewFoscarini банкиры используют Excel, им не нужны программисты;)
Mateusz

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

Неловко читать - гораздо больше проблем, чем неудобно печатать.
Alb

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 байт ,

ОБНОВЛЕНИЕ Комментатор отметил, что существуют специальные условия для цифровых показателей 2: http://en.wikipedia.org/wiki/Mebibyte


«2 мегабайта - это не 2 000 000 байтов». Спросите любого изготовителя жесткого диска с вращающимся диском. (Не внизу, между прочим.)
CVN

@michaelkjorling - это вопрос программирования, а не деловой или маркетинговой. Я согласен с жесткими дисками, но это другая тема. И ай о падших голосах!
г-н Т.А.

1
На самом деле, 2 мегабайта - это 2 000 000 байтов. 2 мегабайта составляют 2 097 152 байта. См en.wikipedia.org/wiki/Mebibyte
vy32

@ vy32 спасибо, никогда не слышал об этом раньше. Обновлю мой ответ, чтобы отразить это.
г-н ТА

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