Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support to BigDecimal in JsonPrimitive#equals #2364

Merged
merged 13 commits into from
Feb 9, 2024

Conversation

MaicolAntali
Copy link
Contributor

@MaicolAntali MaicolAntali commented Apr 1, 2023

Purpose

Fixes #904

Description

Currently a BigDeciamal is cast to a double and this can give issue if the number doesn't fit in it.

double a = getAsNumber().doubleValue();
double b = other.getAsNumber().doubleValue();
return a == b || (Double.isNaN(a) && Double.isNaN(b));

I have implemented a ternary operator to check if a number is a BigDecimal and in that case use the proper equals method.

Checklist

  • New code follows the Google Java Style Guide
  • If necessary, new public API validates arguments, for example rejects null
  • New public API has Javadoc
    • Javadoc uses @since $next-version$
      ($next-version$ is a special placeholder which is automatically replaced during release)
  • If necessary, new unit tests have been added
    • Assertions in unit tests use Truth, see existing tests
    • No JUnit 3 features are used (such as extending class TestCase)
    • If this pull request fixes a bug, a new test was added for a situation which failed previously and is now fixed
  • mvn clean verify javadoc:jar passes without errors

Adds to the JsonPrimitive#equals the possibility to support BigDecimal
Adds test to check if the equals work with BigDecimals. Code snippet from issue google#904
public void testBigDecimalEquals() {
JsonPrimitive small = new JsonPrimitive(1.0);
JsonPrimitive large = new JsonPrimitive(2.0);
assertThat(small.equals(large)).isFalse();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assert probably is overkill because is already tested here:

public void testFloatEqualsBigDecimal() {
JsonPrimitive p1 = new JsonPrimitive(10.25F);
JsonPrimitive p2 = new JsonPrimitive(new BigDecimal("10.25"));
assertThat(p1).isEqualTo(p2);
assertThat(p1.hashCode()).isEqualTo(p2.hashCode());

It's here to be consistent with the code in the issue #904

Replaces the `.equals` method with the `compareTo` in the `JsonPrimitive#equals`

Change the ternary operator from `||` to `&&` so we are sure that both are `BigDecimal`

Implements tests
Copy link
Collaborator

@Marcono1234 Marcono1234 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MaicolAntali, could you please merge main into your branch and fix the new Error Prone warning:

[WARNING] /gson/src/main/java/com/google/gson/JsonPrimitive.java:[294,47] [OperatorPrecedence] Use grouping parenthesis to make the operator precedence explicit
    (see https://errorprone.info/bugpattern/OperatorPrecedence)
  Did you mean 'return (this.value instanceof BigDecimal && other.value instanceof BigDecimal)'?

To me these changes look reasonable, and they should solve #904. I assume there might be some other cases where comparing BigDecimal with primitive values such as double or long might still cause precision loss, but if necessary that can be addressed later.

@eamonnmcmanus, what do you think?

gson/src/main/java/com/google/gson/JsonPrimitive.java Outdated Show resolved Hide resolved
gson/src/main/java/com/google/gson/JsonPrimitive.java Outdated Show resolved Hide resolved
MaicolAntali and others added 4 commits October 26, 2023 17:59
- Extracts `thisAsDouble` & `otherAsDouble` variables to avoid double functions calls.

- Adds a comment to improve the code readability.
@Marcono1234
Copy link
Collaborator

@MaicolAntali, could you please merge main into the branch of this PR, mainly to be sure it passes the formatting checks? Sorry for the trouble.

@MaicolAntali
Copy link
Contributor Author

Don't worry, help it's a pleasure!

@Marcono1234 Marcono1234 mentioned this pull request Nov 18, 2023
9 tasks
Comment on lines +291 to +292
// Uses compareTo to ignore scale of values, e.g. `0` and `0.00` should be considered equal
return this.getAsBigDecimal().compareTo(other.getAsBigDecimal()) == 0;
Copy link
Collaborator

@Marcono1234 Marcono1234 Nov 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As side note / for future reference: I originally proposed using compareTo when this code was also comparing BigDecimal with non-BigDecimal, see #2364 (comment).

However, using compareTo instead of equals here has another advantage: It ensures JsonPrimitive.equals is transitive (as required by Object.equals), at least when wrapping a BigDecimal.

(x equals y and y equals z) implies x equals z

Now let

  • x = new JsonPrimitive(new BigDecimal("0"))
  • y = new JsonPrimitive(0.0d)
  • z = new JsonPrimitive(new BigDecimal("0.00"))

Using compareTo makes sure that equals is transitive. Whereas using BigDecimal.equals here would cause: x equals y and y equals z (because they fall back to double comparison), but x !equals z, which would not be transitive.

Copy link
Member

@eamonnmcmanus eamonnmcmanus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this! I ran it against all of Google's internal tests and everything passed.

@eamonnmcmanus eamonnmcmanus merged commit db61bb0 into google:main Feb 9, 2024
10 checks passed
@MaicolAntali MaicolAntali deleted the fix-904 branch February 9, 2024 17:22
tibor-universe pushed a commit to getuniverse/gson that referenced this pull request Sep 14, 2024
* Adds support to `BigDecimal`

Adds to the JsonPrimitive#equals the possibility to support BigDecimal

* Adds test

Adds test to check if the equals work with BigDecimals. Code snippet from issue google#904

* Implements review comments

Replaces the `.equals` method with the `compareTo` in the `JsonPrimitive#equals`

Change the ternary operator from `||` to `&&` so we are sure that both are `BigDecimal`

Implements tests

* Changes to follow the google-style-guide

* implements review comment

Co-authored-by: Marcono1234 <[email protected]>

* Fixes the `OperatorPrecedence` warn

* Implements code improvements

- Extracts `thisAsDouble` & `otherAsDouble` variables to avoid double functions calls.

- Adds a comment to improve the code readability.

* Implements `BigDecimal` check in the `JsonPrimitive.equals()`

* Formats the code with `spotless:apply`

---------

Co-authored-by: Marcono1234 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BigDecimal equals problem
3 participants