This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
---
|
||||
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!
|
||||
@@ -2,3 +2,7 @@
|
||||
title: Summing Up
|
||||
weight: 6
|
||||
---
|
||||
|
||||
Here I just wanted to apply some of the best practises to improve our code a little better.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user