---
title: SumBigDecimalHex
weight: 6
---

# Task 

- Input data fits into [BigDecimal](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html) data type
- There are decimal and hexadecimal numbers in input
- Hexadecimal numbers has `0x` prefix, so for example `0xbsc` equals `11·10⁻¹²` (mantissa and exponent are integers)
- Input is case insensitive
- Class should be named `SumBigDecimalHex`

## Solution

This is the hardest (and last) modification for this homework.

Let's modify our `SumDoubleHex` `parseNumber` method. This is what it looks now

```java
static double parseNumber(StringBuilder number) {
    if (!number.isEmpty()) {
        String numberString = number.toString();
        if (
            numberString.startsWith("0x") || numberString.startsWith("0X")
        ) {
            if (numberString.contains(".")) {
                return Double.parseDouble(numberString);
            }
            return Long.parseLong(numberString.substring(2), 16);
        } else {
            return Double.parseDouble(numberString);
        }
    } else {
        return 0;
    }
}
```

Let's make some small changes:

Firstly, let's change the output data type to `BigDecimal`. So we will change this line 

```java
static double parseNumber(StringBuilder number) {
```

to this

```java
static BigDecimal parseNumber(StringBuilder number) {
```

Also let's change this line from the parsing of a decimal number option

```java
return Double.parseDouble(numberString);
```

to 

```java
return new BigDecimal(numberString);
```

We will change this line 

```java
return 0;
```

to 

```java
return BigDecimal.ZERO;
```

as we did almost the same thing for `SumBigIntegerOctal` modification.

Also let's change our `numberString` to lowercase letters just for convenience

```
String numberString = number.toString().toLowerCase();
```

> [!NOTE]
> Here we are using [`String.toLowerCase()`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#toLowerCase--) method that converts all the characters to lower case.

So our code will currently look like this

```java
static BigDecimal parseNumber(StringBuilder number) {
    if (!number.isEmpty()) {
        String numberString = number.toString().toLowerCase(); // toLowerCase() added
        if (numberString.startsWith("0x")) {
            numberString = numberString.substring(2);
            if ( /* numberString has "s" in it  */ ) {
                /* somehow parse number */
            }
            /* somehow parse number */
        } else {
            return new BigDecimal(numberString);
        }
    } else {
        return BigDecimal.ZERO;
    }
}
```

> [!NOTE]
> Notice, that we no longer need to check for `numberString` to begin with `"0X"`. Since there can't be an upper case letter in the `numberString` after converting all of its letters to lower case.

> [!NOTE]
> Also in this line 
> ```java
> numberString = numberString.substring(2);
> ```
> we strip off first two symbols (`0x`) in order for correct parsing later.

Let's solve our "comments" one-by-one.

The easiest is to understand if there is an `"s"` in `numberString`. We can do so using [`String.contains(CharSequence s)`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#contains-java.lang.CharSequence-) that returns true if and only if this string contains the specified sequence of char values. In our case the sequence of values will be `"s"`.

Let's apply this logic to our method

```java
static BigDecimal parseNumber(StringBuilder number) {
    if (!number.isEmpty()) {
        String numberString = number.toString().toLowerCase();
        if (numberString.startsWith("0x")) {
            numberString = numberString.substring(2);
            if (numberString.contains("s")) { // <--
                /* somehow parse number */
            }
            /* somehow parse number */
        } else {
            return new BigDecimal(numberString);
        }
    } else {
        return BigDecimal.ZERO;
    }
}
```

> [!NOTE]
> Again, we are not checking for upper case `"S"`, since there are no upper case letters in `numberString` because we used `.toLowerCase()`.

Okay, now let's parse our number if it doesn't contain `"s"`. It's actually very simple. First we need to convert it to `BigInteger` and then pass the result to `BigDecimal`. It will look like this 

```java
// using 16 to convert from hexadecimal to decimal 
// and only then passing to BigDecimal
return new BigDecimal(new BigInteger(numberString, 16)); 
```

> [!IMPORTANT]
> Here, we don't assign `BigInteger` object to any variable so it is used only for conversion from hexadecimal to decimal. After it's value is passed to `BigDecimal` a garbage collector can come and destroy it, since there is no way to access it anymore.

We have only one "unsolved comment" left, but it's going to require a bit of theory. So basically `s` splits our number in two parts: everything on the left is called a mantissa and on the right is exponent. So the form is basically the following

$$
0x\{mantissa\}s\{exponent\}
$$

In order to get the number we will first need to convert each part from hexadecimal to decimal. Exponent represents decimal places. For example `0xABs02`: `AB` in hex equals 171 in decimal, `02` means 2 decimal places, so we shift point (`.`) 2 places left and get 1.71. Mathematically we can shift a point (`.`) by multiplying 10 with some power (in our case exponent). But to shift left we need to first negate the exponent so the point (`.`) shifts left (not right).

So the formula is 

$$
mantissa \cdot 10^{-exponent}
$$

Okay, we are done with theory, but how can we implement it in our code. 

First of all we need somehow to split our `numberString` by `"s"`. To do that we need to get the index at which `"s"` in placed. To do that we can use [`String.indexOf(char ch)`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#indexOf-int-) method that returns the index of the first occurrence of the specified character (in our case `"s"`). Okay, then using [`String.substring()`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#substring-int-) we can split into two parts. Here's what it's gonna look like 

```java
int indexOfS = numberString.indexOf('s');

// all characters before 's', without including 's' itself
String mantissaHexString = numberString.substring(0, indexOfS);

// all characters after 's', without including 's' itself
String exponentHexString = numberString.substring(indexOfS + 1);
```

> [!NOTE]
> Note, that we use `'s'` (not `"s"`) in `indexOf` method. That's because it uses [`char`](https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html) data type as its argument, but `"s"` is a string. So we need single quotes in order to represent `char`.

> [!NOTE]
> When extracting exponent we can use only one argument (`indexOfS + 1`), because it will automatically go to the end of the string itself.

Okay, now that we have `mantissaHexString` and `exponentHexString` we just need to convert them into decimal. That we know how to do using `BigInteger` with base specifier.

```java
BigInteger mantissa = new BigInteger(mantissaHexString, 16);
BigInteger exponent = new BigInteger(exponentHexString, 16);
```

Okay, one thing left is to use the formula that we got

$$
mantissa \cdot 10^{-exponent}
$$

I will write the solution first and explain it right after 

```java
return new BigDecimal(mantissa).scaleByPowerOfTen(exponent.negate().intValueExact());
```

Here we are using [`BigDecimal.scaleByPowerOfTen(int n)`](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#scaleByPowerOfTen-int-) method to basically multiply by \(10^{-exponent}\). Then we use [`BigInteger.negate()`](https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html#negate--) to basically multiply by -1. And finally we use [`BigInteger.intValueExact()`](https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html#intValueExact--) to get the integer value. We do that because `scaleByPowerOfTen` method recieves integer argument (not `BigInteger`).

So our method will look like this

```java
static BigDecimal parseNumber(StringBuilder number) {
    if (!number.isEmpty()) {
        String numberString = number.toString().toLowerCase();
        if (numberString.startsWith("0x")) {
            numberString = numberString.substring(2);
            if (numberString.contains("s")) {
                int indexOfS = numberString.indexOf('s');

                String mantissaHexString = numberString.substring(0, indexOfS);
                String exponentHexString = numberString.substring(indexOfS + 1);
                
                BigInteger mantissa = new BigInteger(mantissaHexString, 16);
                BigInteger exponent = new BigInteger(exponentHexString, 16);

                return new BigDecimal(mantissa).scaleByPowerOfTen(exponent.negate().intValueExact());
            }
            return new BigDecimal(new BigInteger(numberString, 16));
        } else {
            return new BigDecimal(numberString);
        }
    } else {
        return BigDecimal.ZERO;
    }
}
```

Okay, now let's change `main` method accordingly. Let's take `main` method from `SumBigIntegerOctal` modification. Here's what it looks like now 

```java
public static void main(String[] args) {
    BigInteger sum = new BigInteger("0");
    for (String argument : args) {
        StringBuilder number = new StringBuilder();
        for (char c : argument.toCharArray()) {
            if (!Character.isWhitespace(c)) {
                number.append(c);
            } else {
                sum = sum.add(parseNumber(number));
                number = new StringBuilder();
            }
        }

        sum = sum.add(parseNumber(number));
    }

    System.out.println(sum);
}
```

All we need to do is to replace the sum type from `BigInteger` to `BigDecimal`. So full code will look like this 

```java
// SumBigDecimalHex.java

import java.math.BigDecimal;
import java.math.BigInteger;

public class SumBigDecimalHex {
  public static void main(String[] args) {
      BigDecimal sum = new BigDecimal("0");
      for (String argument : args) {
          StringBuilder number = new StringBuilder();
          for (char c : argument.toCharArray()) {
              if (!Character.isWhitespace(c)) {
                  number.append(c);
              } else {
                  sum = sum.add(parseNumber(number));
                  number = new StringBuilder();
              }
          }

          sum = sum.add(parseNumber(number));
      }

      System.out.println(sum);
  }


  static BigDecimal parseNumber(StringBuilder number) {
      if (!number.isEmpty()) {
          String numberString = number.toString().toLowerCase();
          if (numberString.startsWith("0x")) {
              numberString = numberString.substring(2);
              if (numberString.contains("s")) {
                  int indexOfS = numberString.indexOf('s');

                  String mantissaHexString = numberString.substring(0, indexOfS);
                  String exponentHexString = numberString.substring(indexOfS + 1);
                  
                  BigInteger mantissa = new BigInteger(mantissaHexString, 16);
                  BigInteger exponent = new BigInteger(exponentHexString, 16);

                  return new BigDecimal(mantissa).scaleByPowerOfTen(exponent.negate().intValueExact());
              }
              return new BigDecimal(new BigInteger(numberString, 16));
          } else {
              return new BigDecimal(numberString);
          }
      } else {
          return BigDecimal.ZERO;
      }
  }
}
```

This code works correctly and passes all the tests!
