Ноль как константа?

15

Я недавно сталкивался с этой идиомой программирования:

const float Zero = 0.0;

который затем используется в сравнениях:

if (x > Zero) {..}

Может ли кто-нибудь объяснить, действительно ли это более эффективно, доступно для чтения или сопровождения, чем:

if (x > 0.0) {..}

ПРИМЕЧАНИЕ: я могу подумать о других причинах для определения этой константы, мне просто интересно, как ее использовать в этом контексте.

NWS
источник
31
Разработчики планируют перенести код во вселенную, где законы математики разные?
vaughandroid
6
Если серьезно, я не могу придумать ни одной веской причины для этого. Единственное объяснение, которое я могу придумать, - это чрезмерно усердные стандарты кодирования или некоторые разработчики, которые слышали «магические числа плохие», но не понимают, почему (или что могло бы составить магическое число) ...
vaughandroid
@Baqueta - Альтернативная вселенная? Я думаю, что они уже жили там! Что касается магических чисел, я согласен, однако я использую эмпирическое правило, согласно которому Все, кроме 0 и 1, должно быть постоянным.
NWS
1
Если xимеет тип float, то x > 0.0форсирует повышение double, которое может быть менее эффективным. Это не очень хорошая причина для использования именованной константы, просто для того, чтобы убедиться, что ваши константы имеют правильный тип (например 0f, float(0)или decltype(x)(0)).
Майк Сеймур
1
@JoSo: честно говоря, 13.37это не floatтак double. Так что, если вы хотели, floatто вполне возможно, что ваш репетитор был прав. В некоторых контекстах (например, присвоение float) 13.37будет неявно преобразовано в то, floatчто вы хотели, а в других контекстах (например, вывод типа шаблона) это не будет, тогда как static const floatвсегда начинается с того типа, который вы намеревались. Следовательно, более безопасный тип. Имейте в виду, так будет 13.37f! Однако есть и другие причины избегать макроса, кроме «безопасности типов», так что вполне вероятно, что преподаватель дал вам плохой аргумент.
Стив Джессоп

Ответы:

29

Возможные причины: кэширование, присвоение имен или принудительный тип.

Кэширование (не применимо)

Вы хотите избежать затрат на создание объекта во время акта сравнения. В Java пример будет

BigDecimal zero = new BigDecimal ("0.0");

это включает в себя довольно тяжелый процесс создания и лучше обслуживается с использованием предоставленного статического метода:

BigDecimal zero = BigDecimal.ZERO;

Это позволяет проводить сравнения без повторных затрат на создание, поскольку JDM во время инициализации предварительно кэширует BigDecimal.

В случае того, что вы описали, примитив выполняет ту же работу. Это в значительной степени избыточно с точки зрения кэширования и производительности.

Именование (маловероятно)

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

Тип принуждения (скорее всего)

Первоначальный разработчик пытается заставить конкретный тип примитива гарантировать, что сравнения приводятся к их правильному типу и, возможно, к определенному масштабу (числу десятичных знаков). Это нормально, но простое имя «ноль», вероятно, недостаточно подробно для этого варианта использования, при этом ZERO_1DP является более подходящим выражением намерения.

Гэри Роу
источник
2
+1 за тип принуждения. Я добавлю, что в таких языках, как C ++, которые допускают перегрузку операторов, определение константы и ее использование typedefсохранит тип переменной ровно в одном месте и позволит изменить его без изменения кода.
Blrfl
3
Принудительный тип, скорее всего, не то, к чему они стремились, однако это лучшее объяснение того, почему это можно сделать!
NWS
7
Для форсирования типа, я бы предпочел просто использовать 0.0f.
Свиш
Принудительный тип может иногда быть полезен в vb.net, где выполнение побитовых операторов над байтами дает байтовый результат. Сказать byteVar1 = byteVar2 Or CB128кажется немного лучше, чем byteVar1 = byteVar2 Or CByte(128). Конечно, было бы лучше иметь правильный числовой суффикс для байтов. Поскольку C # продвигает операнды побитовых операторов, intдаже если результат гарантированно помещается в a byte, проблема здесь не так актуальна.
суперкат
Я не уверен в том, чтобы иметь нулевое постоянное имя для «0», но иногда это помогает в удобочитаемости кода; например, наличие этой константы для нуля - «ROOT_TYPE_ID = 0» поможет написать утверждение типа if (id! = ROOT_TYPE_ID) {..}
Tech Junkie
6

Это из-за "Toolg Nagging"

Возможная причина, которую я не вижу в этом списке, заключается в том, что многие качественные инструменты отмечают использование магических чисел . Часто плохая практика - бросать магические числа в алгоритм, не делая их ясно видимыми для последующего изменения, особенно если они дублируются в нескольких местах кода.

Таким образом, хотя эти инструменты и подходят для обозначения таких проблем, они часто генерируют ложные срабатывания в ситуациях, когда эти значения безвредны и, скорее всего, являются статическими или просто являются значениями инициализации.

И когда это происходит, иногда вы сталкиваетесь с выбором:

  • помечая их как ложные срабатывания, если это позволяет инструмент (обычно со специально отформатированным комментарием, что раздражает людей, НЕ использующих инструмент)
  • или извлечение этих значений в константы, независимо от того, имеет ли это значение или нет.

О производительности

Это зависит от языка, который я предполагаю, но это довольно распространено в Java и не влияет на производительность, так как значения встроены во время компиляции, если они являются реальными константами static final. Это не повлияет на C или C ++, если они объявлены как константы или даже как макросы препроцессора.

haylem
источник
5

Это может иметь смысл, поскольку оно явно определяет Zeroтип float.

По крайней мере в C и C ++ значение 0.0имеет типа double, в то время как эквивалентное floatэто 0.0f. Таким образом, если xвы сравниваете это тоже всегда floatпоговорка

x > 0.0

в то время как на самом деле продвигать, xчтобы doubleсоответствовать типу, 0.0который может привести к проблемам (особенно с тестами на равенство). Конечно, сравнение без конвертации будет

x > 0.0f

который делает так же, как

float Zero = 0.0; // double 0.0 converted to float  
x > Zero

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

Бенджамин Банье
источник
1

Прежде всего, здесь ноль определяется как float, а не int. Конечно, это ничего не влияет на сравнение, но в других случаях, когда используется эта константа, это может иметь значение.

Я не вижу другой причины, почему Zeroздесь объявлена ​​константа. Это просто какой-то стиль кодирования, и лучше следовать этому стилю, если он используется везде в этой определенной программе.

superM
источник
1

Это почти точно так же эффективно во время выполнения (если ваш компилятор не очень примитивен) и очень немного менее эффективно во время компиляции.

Что касается того, является ли это более читабельным, чем x > 0... помните, что есть люди, которые честно, искренне думают, что COBOL была отличной идеей и удовольствием работать с ними, а также есть люди, которые думают точно так же о C. (По слухам, это то, что даже существуют некоторые программисты с таким же мнением о C ++!) Другими словами, вы не достигнете общего согласия по этому вопросу, и, вероятно, не стоит бороться.

Килиан Фот
источник
0

[is] это действительно более эффективно, доступно для чтения или сопровождения, чем:

if (x > 0.0) {..}

Если вы писали общий (т.е. не специфичный для типа) код, то вполне возможно. zero()Функция может применяться к любому алгебраическому типа или любому типу , который представляет собой группу WRT дополнения. Это может быть целое число, это может быть значение с плавающей запятой, это может быть даже функция, если ваша переменная, скажем, сама является функцией в некотором линейном пространстве (например, x является линейной функцией вида z -> a_x * z + b_x), а затем zero()предоставляет функции с a и b оба zero()базового типа.

Таким образом, вы ожидаете такой код, скажем, на C ++, возможно (хотя a zero()не очень распространенный AFAIK), или на Julia, и, возможно, на других языках.

einpoklum
источник