diff --git a/content/courses/prog-intro/homeworks/sum/SumBigDecimalHex/SumBigDecimalHex.java b/content/courses/prog-intro/homeworks/sum/SumBigDecimalHex/SumBigDecimalHex.java new file mode 100644 index 0000000..dbd70a4 --- /dev/null +++ b/content/courses/prog-intro/homeworks/sum/SumBigDecimalHex/SumBigDecimalHex.java @@ -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; + } + } +} + diff --git a/content/courses/prog-intro/homeworks/sum/SumBigDecimalHex/_index.md b/content/courses/prog-intro/homeworks/sum/SumBigDecimalHex/_index.md new file mode 100644 index 0000000..924fdb1 --- /dev/null +++ b/content/courses/prog-intro/homeworks/sum/SumBigDecimalHex/_index.md @@ -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! diff --git a/content/courses/prog-intro/homeworks/sum/conclusion/_index.md b/content/courses/prog-intro/homeworks/sum/conclusion/_index.md index 95bc33d..9160ef1 100644 --- a/content/courses/prog-intro/homeworks/sum/conclusion/_index.md +++ b/content/courses/prog-intro/homeworks/sum/conclusion/_index.md @@ -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. + + diff --git a/hugo.toml b/hugo.toml index 55fd5d0..3982a98 100644 --- a/hugo.toml +++ b/hugo.toml @@ -15,3 +15,15 @@ theme = 'github.com/imfing/hextra' [params] description = 'Learn Java programming' + + [params.math] + engine = 'katex' + +[markup] + [markup.goldmark] + [markup.goldmark.extensions] + [markup.goldmark.extensions.passthrough] + enable = true + [markup.goldmark.extensions.passthrough.delimiters] + block = [['\[', '\]'], ['$$', '$$']] + inline = [['\(', '\)']] diff --git a/public/categories/index.html b/public/categories/index.html index 7ca207f..339d1f2 100644 --- a/public/categories/index.html +++ b/public/categories/index.html @@ -192,6 +192,14 @@ diff --git a/public/courses/index.html b/public/courses/index.html index 780e28d..0255ab1 100644 --- a/public/courses/index.html +++ b/public/courses/index.html @@ -193,6 +193,14 @@ @@ -297,6 +321,14 @@ diff --git a/public/courses/paradigms/index.html b/public/courses/paradigms/index.html index be8a579..52710c8 100644 --- a/public/courses/paradigms/index.html +++ b/public/courses/paradigms/index.html @@ -192,6 +192,14 @@ @@ -297,6 +321,14 @@ diff --git a/public/courses/prog-intro/homeworks/index.html b/public/courses/prog-intro/homeworks/index.html index 3641fe3..d183603 100644 --- a/public/courses/prog-intro/homeworks/index.html +++ b/public/courses/prog-intro/homeworks/index.html @@ -193,6 +193,14 @@ @@ -297,6 +321,14 @@ diff --git a/public/courses/prog-intro/homeworks/sum/conclusion/index.html b/public/courses/prog-intro/homeworks/sum/conclusion/index.html index 78f68c1..2c04e8e 100644 --- a/public/courses/prog-intro/homeworks/sum/conclusion/index.html +++ b/public/courses/prog-intro/homeworks/sum/conclusion/index.html @@ -192,6 +192,14 @@