How to make money with Java and C++ : IEEE 754 and the real life : ordering zeros.
Tags: java, cpp
Published: 2022-01-06 21:52:07

TL;DR => use [BigDecimal](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html) for Java and [Boost.Multiprecision](https://www.boost.org/doc/libs/1_74_0/libs/multiprecision/doc/html/index.html) or [std::decimal](https://gcc.gnu.org/onlinedocs/gcc-11.2.0/libstdc++/api/a06011.html) for C++ to handle money, detailed explanation with examples in the [next article](https://dev.to/hexfloor/how-to-make-money-with-java-and-c-ieee-754-and-the-real-life-010203-yes-but-how-5244)

Now that we have a spoiler and the problem is solved, let's see why we cannot just use `double` without trouble.
The word itself is a bit oldish, for zoomers an existence of non 64 bit processors should sound like a floppy disk.
Here and after Java stands for Java 17 and C++ stands for C++ 20, if not specified differently.
Let's start from basics, and here is [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754), this is a technical standard for the floating-point arithmetic's, several concepts are to be mentioned : [infinity](https://en.wikipedia.org/wiki/Infinity), [NaN](https://en.wikipedia.org/wiki/NaN), [signed zero : -0, +0](https://en.wikipedia.org/wiki/Signed_zero).
Both Java and C++ implement IEEE 754 for float and double types, it has been a bit of a challenge for Java regarding the double rounding problem, see here: [strictfp](https://en.wikipedia.org/wiki/Strictfp).
The first surprise arises when an engineer sees that there are `-0` and `0` and they are equal, are they ? They are equal as per IEEE 754, so, if I sort an array in Java and C++, am I going to get `-0` and `0` mixed ?
And the answer is different for both languages, let's see.

## IEEE 754 : signed zero in C++

Check the [three-way comparison](https://en.cppreference.com/w/cpp/language/operator_comparison).
Here is an example : 

```
#include <compare>
#include <iostream>
 
int main()
{
    double foo = -0.0;
    double bar = 0.0;
    
    std::cout<< (foo==bar ? "-0 and 0 are equal" : "-0 and 0 are NOT equal" ) 
<<std::endl;
 
    auto res = foo <=> bar;
    if (res < 0)
        std::cout << "-0 is less than 0";
    else if (res > 0)
        std::cout << "-0 is greater than 0";
    else // (res == 0)
        std::cout << "-0 and 0 are equal";
}
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n5l8fs76p8h1g45ndrml.png)
And the consequence : 

 

```
#include <iostream>
#include <iterator>
#include <bits/stdc++.h>


int main()
{
    auto show_me_the_money = [](auto& arr) {
        std::copy(std::begin(arr), std::end(arr), 
            std::ostream_iterator<double>(std::cout, " "));
        std::cout<<std::endl;
    };
    
    double arr[] = {1.0, -0.0, 0.0, -0.0, -1.0};
    show_me_the_money(arr);
    std::sort(arr, arr + sizeof(arr)/sizeof(arr[0]), std::less<double>());
    show_me_the_money(arr);
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t4cce0e0ztav6978a6ij.png)

Let me show you few tricks on how can we make precede the `-0` before `0`.
If C++ had been taught in Hogwarts it would have been taught by Severus Snape, as you can cast pretty much everything. Here are different ways to get a sign of a zero : 

```
#include <iostream>
#include<math.h>  
union u {
    double d;
    uint64_t i;
};
int main()
{
    auto cast = [](auto& d) {
        uint64_t i = *reinterpret_cast<uint64_t *>(&d);
        return i;
    };
    auto unite = [](auto& d) {
        u _u;
        _u.d =d;
        return _u.i;
    };
    double foo = -0.0;
    double bar = 0.0;
    std::cout<<cast(foo)<<" "<<cast(bar)<<std::endl;
    std::cout<<unite(foo)<<" "<<unite(bar)<<std::endl;
    std::cout<<signbit(foo)<<" "<<signbit(bar)<<std::endl;
}
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4w3c4scgsaikbl5dpbfx.png)
I'll use the `signbit` function : 
```
#include <iostream>
#include <iterator>
#include <bits/stdc++.h>
#include<math.h>  

int main()
{
    auto show_me_the_money = [](auto& arr) {
        std::copy(std::begin(arr), std::end(arr), 
            std::ostream_iterator<double>(std::cout, " "));
        std::cout<<std::endl;
    };
    std::less<double> less_double;
    auto less_zero = [&]( const auto& lhs, const auto& rhs ){
        auto res = less_double(lhs, rhs);
        return res != 0 ? res : signbit (lhs) > signbit(rhs);
    };
    
    double arr[] = {1.0, -0.0, 0.0, -0.0, -1.0};
    show_me_the_money(arr);
    std::sort(arr, arr + sizeof(arr)/sizeof(arr[0]), less_zero);
    show_me_the_money(arr);
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/854tjq56qpx3rea6x2zm.png)
 
 
 
With a custom comparator I can order `-0` and `0`. The question is, how can we convert `-0` to `0` ? 
The answer is pretty simple : you have to add `0`, let me show you : 

```
#include <iostream>
#include <iterator>
#include <bits/stdc++.h>


int main()
{
    auto show_me_the_money = [](auto& arr) {
        std::copy(std::begin(arr), std::end(arr), 
            std::ostream_iterator<double>(std::cout, " "));
        std::cout<<std::endl;
    };
    
    double arr[] = {1.0, -0.0, 0.0, -0.0, -1.0};
    show_me_the_money(arr);
    std::for_each(std::begin(arr), std::end(arr), [](double& d) { d+=0.0;});
    std::sort(arr, arr + sizeof(arr)/sizeof(arr[0]), std::less<double>());
    show_me_the_money(arr);
}
```
 ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g7d9spygv37hypq6j0he.png)

Alright, we have managed to handle zeros in C++.
 
## IEEE 754 : signed zero in Java

Things in Java world looks simpler, see the source code of `Double.compare` method for the details.
I've used [w3schools](https://www.w3schools.com/java/tryjava.asp?filename=demo_compiler) Java compiler for tests :

```
public class Main {
    public static void main(String args[]) {
      double foo = -0.0;
      double bar = 0.0;

      System.out.println("-0 and 0 are equal " + (foo == bar));
      System.out.println("-0 and 0 are ordered " + Double.compare(foo,bar));
    }
}
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/18k9ikau3p892shkiljg.png)
 
Sorting zeros in an array is straight forward : 

```
import java.util.*;

public class Main {
    public static void main(String args[]) {
      double [] arr = {1.0, -0.0, 0.0, -0.0, -1.0};
      System.out.println(Arrays.toString(arr));
      Arrays.sort(arr);
      System.out.println(Arrays.toString(arr));
    }
}
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/awj29k3c7zu0av35df3z.png)
 
Same adding `0` trick applies : 

```
import java.util.*;

public class Main {
    public static void main(String args[]) {
      double [] arr = {1.0, -0.0, 0.0, -0.0, -1.0};
      System.out.println(Arrays.toString(arr));
      arr = Arrays.stream(arr).map(v -> v+= 0.0).toArray();
      System.out.println(Arrays.toString(arr));
    }
}
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n5jqur4yi69nlyohwa59.png)

## Summary

Both C++ and Java implement IEEE 754 and following the standard you have two zeros which are equal. The results of a software we do might be used elsewhere, and having values with minus sign to be mixed with values with no sign can lead to the dramatic consequences. Still neither double nor float should be used for the money handling and I'll cover the reasons in the next article. Take care!