Bitwise operators in Java: unpacking ambiguities
Tags: java, programming, coding
Published: 2024-06-21 11:27:13

Author: Kirill Epifanov

The "&" and "\|" operators are pretty straightforward and unambiguous when applied correctly\. But do you know all the implications of using bitwise operators instead of logical ones in Java? In this article, we will examine both the pros of this approach in terms of performance and the cons in terms of code readability\.

![](https://import.viva64.com/docx/blog/1135_bitwise_or/image1.png)

## Before we start

This is the third and the final article of the series\. It's written based on the results of checking [DBeaver](https://github.com/dbeaver/dbeaver) version 24 with the help of the PVS\-Studio static analyzer\. The tool detected a few suspicious code fragments that caught our development team's attention and prompted us to cover them in several articles\. If you haven't read the previous ones, you can find them here:

* [Volatile, DCL, and synchronization pitfalls in Java](https://pvs-studio.com/en/blog/posts/java/1128/)
* [How template method can ruin your Java code](https://pvs-studio.com/en/blog/posts/java/1132/)
* Bitwise operators in Java: unpacking the ambiguities \(you are here\)

## Once upon a time

While wading through the warnings issued by the analyzer, we encountered a warning for logical expressions that contained bitwise operations in an *if* block\. The analyzer claims it to be a suspicious code snippet and issues a warning of the second \(medium\) level — [V6030](https://pvs-studio.com/en/docs/warnings/v6030/)\. Here are some of the PVS\-Studio messages:

The [ExasolSecurityPolicy\.java\(123\)](https://github.com/dbeaver/dbeaver/blob/f0c87ca1251621e3803a9a807dd4336bfdfef0c1/plugins/org.jkiss.dbeaver.ext.exasol/src/org/jkiss/dbeaver/ext/exasol/model/security/ExasolSecurityPolicy.java#L123) file\.

```cpp
public ExasolSecurityPolicy(....) {
  ....
  String value = JDBCUtils.safeGetString(dbResult, "SYSTEM_VALUE");
  
  if (value.isEmpty() | value.equals("OFF"))
    this.enabled = false;
  } else {
    assignValues(ExasolSecurityPolicy.parseInput(value));
  }
}
```

The analyzer reports that the *value\.equals\("OFF"\)* method is called even if *value\.isEmpty\(\)* is true, which is meaningless\. The warning:

[V6030](https://pvs-studio.com/en/docs/warnings/v6030/) The method located to the right of the '\|' operator will be called regardless of the value of the left operand\. Perhaps, it is better to use '\|\|'\.

The [ExasolConnectionManager\.java\(151\)](https://github.com/dbeaver/dbeaver/blob/f0c87ca1251621e3803a9a807dd4336bfdfef0c1/plugins/org.jkiss.dbeaver.ext.exasol/src/org/jkiss/dbeaver/ext/exasol/manager/ExasolConnectionManager.java#L151) file\.

```cpp
@Override
protected void addObjectModifyActions(....) {
  ....
  // url, username or password have changed
  if (com.containsKey("url") | com.containsKey("userName") |
      com.containsKey("password"))
  {
    // possible loss of information - warn
    ....
    if (!(con.getUserName().isEmpty() | con.getPassword().isEmpty())) {
      ....
    }
  }
}
```

Here we see sequential checks of Map *com* for *url* keys, *userName*, or *password*\. Then, the absence of one of the connection parameters is checked similarly\. As a result, we receive the same old message yet again:

[V6030](https://pvs-studio.com/en/docs/warnings/v6030/) The method located to the right of the '\|' operator will be called regardless of the value of the left operand\. Perhaps, it is better to use '\|\|'\.

The documentation of the PVS\-Studio analyzer states that the function returning a *boolean* value is located to the right of the bitwise operator, which is probably a typo\. I got curious about this code style\. After all, if there was a single warning of this kind, I'd think it was a typo\. However, the project shows 16 such code fragments, and I wondered if there's a double dip in such an error pattern\.

## Operators and how do they differ

Let's take a quick dive into the theory\. I think it's reasonable to skip the long list of Java operators and focus directly on the bitwise and logical ones we need:

* && — logical AND
* \|\| — logical OR
* & — bitwise AND
* \| — bitwise OR

Every theoretical Java website states that bitwise operations, unlike logical ones, always execute both parts of an expression\. This code is an example of using the bitwise operator:

```cpp
public static void main(String[] args) {
    // updateA() and updateB() is always executed
    if (updateA() & updateB()) { 
      // Doing something
    }
}

public boolean updateA() {
    // Changing the state...
    return ....;
}

public boolean updateB() {
    // Changing the state...
    return ....;
}
```

If we replace the bitwise operator with the logical one and the first condition returns *false*, the execution of the checks stops\. Let's get a bit imaginative and explore examples that alter the inner state\. In the following code, if *prepareConnection\(\)* returns *false*, the *establishConnection\(\)* method isn't executed\.

```cpp
public static void main(String[] args) {
    // establishConnection() is executed only if prepareConnection() -> false
    if (prepareConnection() && establishConnection()) { 
        ....
    }
}

public boolean prepareConnection() {
    ....
}

public boolean establishConnection() {
    ....
}
```

So, if the operator can determine the result of the operation based on the evaluation of the left argument alone, the second argument is not evaluated as it's deemed unnecessary\. This mechanism is called **short\-circuit evaluation**\. Perhaps one of the most popular use cases for short circuits is to check for *null* before using a variable\.

```cpp
public boolean testString(String str) {
    return str != null && str.equals("test");
}
```

At this point, it may seem that the DBeaver developers made a mistake that increased the number of checks done in the *if* blocks\. This could have been due to a typo or just plain unawareness\. But that's where the **ambiguity** comes into play\.

## The far it goes\.\.\.

Let's dig into the details\. It turns out that a bitwise operation can be faster than a logical operation, depending on the context\. This is mainly because bitwise operators don't involve a **branch prediction** mechanism\.

> Branch prediction is a unit included in the CPU\. It enables prefetching of branch instructions as well as execution of instructions placed after a branch \(e\.g\., an *if* block\) before it is executed\. The branch predictor is an integral part of all modern pipelined processors\. It enables the optimization of computing resources\.

As a side note, I should mention that branch prediction is closely related to **speculative execution**\. In short, it enables the processor to perform some operations on a stream of data ahead of time without waiting until it determines whether data is taken or not\. If lucky and data is taken, we get a performance boost\. If not, the changes are rolled back, incurring overhead\.

So, modern processors have instruction pipelining\. Each executed instruction has several stages: fetch, decode, execute, and save\.

* The fetch stage: the processor retrieves the instruction from memory\.
* The decode stage: the processor interprets the instruction\.
* The execute stage: the processor executes the operation specified by the instruction\.
* The save stage: the processor saves the result of the execution\.

Within a processor, different operations can be executed in parallel: one instruction is being executed while another is being fetched from memory\. The table below can illustrate this:

![](https://import.viva64.com/docx/blog/1135_bitwise_or/image2.png)

Everything seems fine until we encounter a branch instruction along the way\. And that's where our pipelined processor is going to hit a snag\. If a conditional operator appears in the middle of the pipeline execution, the processor can't fetch the next operations because they depend on the outcome of the logical block\.

![](https://import.viva64.com/docx/blog/1135_bitwise_or/image3.png)

That's why modern processors try to **predict the execution flow** and anticipate the instructions to be executed next\. It optimizes code execution if it's not explicitly specified\. For example:

```cpp
private boolean debug = false;

public void test(....) {
    ....
    if (debug) {
        log("....");
    }
    ....
}
```

In this case, the processor \(though the compiler and JVM can also make adjustments, which we'll ignore for now\) can predict with high probability that the *log* method call won't occur\. So, the processor won't prepare to execute this code based on execution statistics\. However, if the processor makes an error, the pipeline needs to be rebuilt, which **may impact performance**\.

Bitwise operations lack this mechanism: **they do not involve branches** and are devoid of their overhead\.

To confirm this, I ran a tiny benchmark based on the source code provided by a developer known as [Rostor](https://stackoverflow.com/users/166235/rotsor) \([source](https://stackoverflow.com/questions/3076078/check-if-at-least-two-out-of-three-booleans-are-true/)\)\.

<spoiler title="Code">

[Uploaded](https://gist.github.com/TheLivan/9f301c4ded3a1d95807c18e9da35bbe1) to GitHub Gist



Here we call methods with logical expressions one by one, using both bitwise and logical operations\.

</spoiler>


The research results on my PC surprised me\. In an average test without using functional interfaces and other OOP tricks, bitwise operations appear to be up to 40 percent faster than logical ones\. You may read the chart below displaying the time taken for each individual operation\.

![](https://import.viva64.com/docx/blog/1135_bitwise_or/image4.png)

The test compares conjunctions and disjunctions, both separately and all together\. The biggest ones are shown at the top for quick reference\. One immediate conclusion is apparent: logical operators turn out to be slower in this case\.

Why? Branch prediction can often be erroneous and cause performance issues\. In such a case, the bitwise operator clearly gains in execution performance due to the absence of this mechanism\.

However, it's worth mentioning that this test is not flawless, and outcomes can still vary based on the processor design, performance, and testing conditions, which we'll delve into shortly\. I'd love to discuss the test results and hear your thoughts on them in the comments\. Nevertheless, it now seems that bitwise operators can run faster\.

## Why, exactly, is that bad?

After reading all this, you might wonder, "Why does this diagnostic exist in your analyzer if such code can be executed faster? Even if a programmer makes a typo, isn't it just a [code smell](https://en.wikipedia.org/wiki/Code_smell)?"

I'm still against such code, and here's why:

**First**, let me show you two interesting warnings from the same project that were among several other "false positives"\.

The [ExasolTableColumnManager\.java\(79\)](https://github.com/dbeaver/dbeaver/blob/2102cbe2629742e2b8e3bd054a92e81b111ea9a4/plugins/org.jkiss.dbeaver.ext.exasol/src/org/jkiss/dbeaver/ext/exasol/manager/ExasolTableColumnManager.java#L79) and [DB2TableColumnManager\.java\(77\)](https://github.com/dbeaver/dbeaver/blob/f0c87ca1251621e3803a9a807dd4336bfdfef0c1/plugins/org.jkiss.dbeaver.ext.db2/src/org/jkiss/dbeaver/ext/db2/manager/DB2TableColumnManager.java#L77) files\.

```cpp
@Override
public boolean canEditObject(ExasolTableColumn object) {
    ExasolTableBase exasolTableBase = object.getParentObject();
    if (exasolTableBase != null &
        exasolTableBase.getClass().equals(ExasolTable.class)) {
        return true;
    } else {
        return false;
    }
}
```

[V6030](https://pvs-studio.com/en/docs/warnings/v6030/) The method located to the right of the '&' operator will be called regardless of the value of the left operand\. Perhaps, it is better to use '&&'\.

This practice sets the stage for** an error and a *NullPointerException***\.

In this case, it's worth considering the side effects and being careful\. Sometimes it's easy to overlook the fact that you're writing code incorrectly\. In the above case, if the *exasolTableBase* parameter turns out to be *null*, an exception is thrown\. And honestly, I don't think the developer meant for the program to crash over a simple check for editability :\)

Besides, one of the developers copied the erroneous code and pulled it into another program module\. Let's add copy\-pasting to the list of sins\. That's why there are two erroneous files above\.

**Second**, another drawback is that we **lose short\-circuit evaluation**, which often improves the performance of the operation\.

For example, as in this case, for which the analyzer also issued a warning:

The [ExasolDataSource\.java\(950\)](https://github.com/dbeaver/dbeaver/blob/7b0fb97265d183cfd5ec2a9524583caa8e76abdb/plugins/org.jkiss.dbeaver.ext.exasol/src/org/jkiss/dbeaver/ext/exasol/model/ExasolDataSource.java#L952) file\.

```cpp
@Override
public ErrorType discoverErrorType(@NotNull Throwable error) {
  String errorMessage = error.getMessage();
  if (errorMessage.contains("Feature not supported")) {
    return ErrorType.FEATURE_UNSUPPORTED;
  } else if (errorMessage.contains("insufficient privileges")) {
    return ErrorType.PERMISSION_DENIED;
  } else if (
      errorMessage.contains("Connection lost") | 
      errorMessage.contains("Connection was killed") | 
      errorMessage.contains("Process does not exist") | 
      errorMessage.contains("Successfully reconnected") | 
      errorMessage.contains("Statement handle not found") |
      ....
      )
  {
    return ErrorType.CONNECTION_LOST;
  }
  return super.discoverErrorType(error);
}
```

[V6030](https://pvs-studio.com/en/docs/warnings/v6030/) The method located to the right of the '\|' operator will be called regardless of the value of the left operand\. Perhaps, it is better to use '\|\|'\.

In this method, the developer checks the *errorMessage* string and returns the error type\. Everything seems fine, but when using a bitwise operator in an *else if* statement, we lose the short\-circuit evaluation optimization\. This is obviously a bad thing, because if any substring is found, we could jump straight to *return* instead of checking the other options\.

**Third**, there's no guarantee bitwise operations will be faster for you\. And if it will, that's just a **micro\-optimization** of a few nanoseconds\.

**Fourth**, the synthetic test above succeeded because the provided values exhibit a **normal distribution** and show no apparent patterns\. In the test, branch prediction only makes it worse\.

So, if the branch predictor operates correctly, bitwise operations cannot catch up with the performance of logical operations\. This case is **very** **specific**\.

To sum up all of the above, the **final** drawback is that constant bitwise checks may seem unconventional from a **code\-writing style** perspective\. Especially when the code involves actual bitwise operations\. It's easy to picture the reaction of a modern Java programmer who encounters a bitwise operator in an *if* statement during code review\.

<video>https://import.viva64.com/docx/blog/1135_bitwise_or/image5.mp4</video>

## Conclusion

That's all for today\. I have investigated the relevance of replacing logical operators with bitwise operators, as well as the applicability of the analyzer's diagnostic rule\. And you've gained some insights into our favorite Java and its practices =\)

If you'd like to search for this or other errors in your project as well, you can try PVS\-Studio for free [at this link](https://pvs-studio.com/en/pvs-studio/try-free/?utm_source=website&utm_medium=devto&utm_campaign=article&utm_content=1135)\.