Подтвердить что ты не робот

Проверка удвоений на вопросы равенства и сонара

Мы проверяем качество нашего кода с помощью Sonar, и Sonar нашел код, который сравнивает float или double для равенства с постоянным значением, подобным этому:

if (x == 0.0) { … }

Значение, которое переменная сравнивается с (0.0), является постоянной, и в случае, если переменная может быть равна этому значению, значение также не вычисляется, а устанавливается только через константу. Обычно это используется для проверки того, была ли переменная еще не установлена ​​или все еще находится в состоянии инициализации, e. г. -1.0 может использоваться для "еще не установленного" в случаях, когда значение может быть только положительным.

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

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

Я вижу несколько вариантов:

  • Извлеките "test-for-unset" в специальную тестовую функцию; но это уменьшит количество случаев (до 1), а не проблему вообще.
  • Отметьте код для Sonar, чтобы игнорировать его с помощью специального декоратора. Но мы хотели бы избежать использования таких декораторов.
  • Скрыть сравнение позади sth, например (0.0 <= x && x <= 0.0) или !(x != 0.0) (что в настоящее время кажется подходящим для Sonar).
  • Вызов Double.doubleToRawLongBits() для сравнения битов таких значений: (Double.doubleToRawLongBits(x) != Double.doubleToRawLongBits(0.0)).
  • Другие идеи?

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

4b9b3361

Ответ 1

Я бы воспользовался вторым вариантом:

Отметьте код для Sonar, чтобы игнорировать его со специальным декоратором.

Не работайте с инструментами анализа статического кода. Они не идеальны, и нет ничего плохого в том, чтобы заставить их заткнуться. Моя личная практика при использовании аннотаций, таких как @SuppressLint, - это включить комментарий, объясняющий, почему я его использую.

Тем не менее, я бы создал константу, поэтому код более понятен:

private static final double UNINITIALIZED = 0.0;
if (x == UNINITIALIZED) { … }

Ответ 2

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

Ответ 3

Если вы используете только эти константы и сравнения для неинициализированных значений, один параметр - установить поля Double.NaN и использовать Double.isNaN() для сравнения. Например:.

double notYetInitialized = Double.NaN;
if (Double.isNaN(notYetInitialized)) {
        // handle uninitialized value
}

Это делает (некоторые) смысл при чтении кода - можно сказать, что неинтеллизированное значение "не будет числом". И я не могу представить, что у Сонара будет проблема с этим.

Ответ 4

Существует также правильный способ сделать это:

private static final double EPSILON = 0.000000000000001;
if (Math.abs(x) < EPSILON) {

}

Ответ 5

Я знаю, что это действительно старая статья, но я столкнулся с той же проблемой и решил ее, используя метод Equals вместо оператора "==".

if (x.Equals(0.0)) { … }

Таким образом, SonarQube не будет жаловаться.

До свидания.

Ответ 6

Предложение: Одним из хороших способов было бы использовать BigDecimal для проверки равенства/не равенства на 0:

BigDecimal balance = pojo.getBalance();//get your BigDecimal obj

0 != balance.compareTo(BigDecimal.ZERO)

Объяснение:

Функция compareTo() сравнивает этот BigDecimal с указанным BigDecimal. Этим методом два объекта BigDecimal, равные по значению, но имеющие разный масштаб (например, 2.0 и 2.00), считаются равными. Этот метод предоставляется в предпочтении индивидуальным методам для каждого из шести операторов сравнения boolean (<, ==, >, >=, !=, <=). Предложенная идиома для выполнения этих сравнений: (x.compareTo(y) <op> 0), где один из шести операторов сравнения.

(Благодаря документации SonarQube)

Математика с плавающей точкой неточна из-за проблем хранения таких значений в двоичном представлении. Хуже того, математика с плавающей запятой не ассоциативна; нажимайте float или double через ряд простых математических операций, и ответ будет отличаться в зависимости от порядка этих операций из-за округления, которое происходит на каждом шаге.

Даже простые назначения с плавающей запятой не просты:

float f = 0.1; // 0.100000001490116119384765625
double d = 0.1; // 0.1000000000000000055511151231257827021181583404541015625

(Результаты будут зависеть от настроек компилятора и компилятора);

Следовательно, использование операторов равенства (==) и неравенства (! =) по плавающим или двойным значениям почти всегда является ошибкой. Вместо этого лучше всего избегать сопоставлений с плавающей запятой. Если это невозможно, вам следует рассмотреть возможность использования одного из чисел с плавающей запятой Java, таких как BigDecimal, которые могут корректно обрабатывать сравнения с плавающей запятой. Третий вариант - смотреть не на равенство, а на то, достаточно ли это значение. То есть сравните абсолютную величину разницы между сохраненным значением и ожидаемым значением с допустимой погрешностью. Обратите внимание, что это не распространяется на все случаи (например, NaN и Infinity).

Ответ 7

Как насчет приведения в (int)?! Это должно иметь тот же результат, что и вы хотите проверить, равна ли оно.

Если у вас есть расчет:

double a = c - b 
boolean x = (int)(a*100) == 0