1. Mathematics

Integers and floating-point numbers already appeared in the first exercises; we were using the common mathematical operators to crunch numbers. But Java does not have an operator for everything, and so the class Math offers further methods and constants; early on we used Math.random(), for example. Throughout this chapter’s upcoming tasks, our focus will be on various rounding techniques, particularly exploring the methods offered by the Math class. In addition, the java.math package provides two classes that can be used to represent arbitrarily large numbers — there are tasks for these as well.

Prerequisites

  • know rounding methods of the class `Math

  • know BigInteger and `BigDecimal

Data types used in this chapter:

1.1. The class Math

The class Math contains numerous mathematical functions. It is also important to look at wrapper classes, for example, or at the Scanner or Formatter when it comes to converting a number to a string or parsing a string so that there is a primitive type at the end again.

1.1.1. Check if Tin Tin cheated on rounding. ⭐

The accountant Tin Tin does the accounting for Captain CiaoCiao of income and expenses. She gets positive and negative floating-point values and writes down the total as a rounded integer for a summary at the end. Captain CiaoCiao suspects that Tin Tin is not being completely honest and is pocketing the penny amounts when she should use commercially rounding. As a reminder, if the number at the first omitted decimal place is a 0, 1, 2, 3, or 4, round down; if it is 5, 6, 7, 8, or 9, round up.

To test his guess, Captain CiaoCiao needs a program that can sum and check different rounding modes.

Task:

  • Given an array of floating-point numbers (positive and negative) and the sum converted to an integer by Tin Tin.

  • Captain CiaoCiao wants to find out which rounding was used to form the integer of the sum. Therefore, the elements in the array are to be summed and compared to Tin Tin’s sum. The rounding is done after the numbers are added.

  • Implement a method RoundingMode detectRoundingMode(double[] numbers, int sum) that gets a double array of numbers and the sum of Tin Tin and checks which rounding mode was used.

    • To allow the rounding mode to be represented, introduce an enumeration type:

      enum RoundingMode {
        CAST, ROUND, FLOOR, CEIL, RINT, UNKNOWN;
      }
    • The enumeration elements represent different rounding modes:

    • (int), that is, a type conversion.

    • (int) Math.floor(double)

    • (int) Math.ceil(double)

    • (int) Math.rint(double)

    • (int) Math.round(double)

  • Which rounding is bad for Captain CiaoCiao and good for Tin Tin? Which variation could Tin Tin use to cheat?

Example:

  • The call might look like this:

    double[] numbers = { 199.99 };
    System.out.println( detectRoundingMode( numbers, 200 ) );

Notes:

  • There is an enumeration type RoundingMode in the java.math package, but for our case, it does not fit the task.

  • It may well happen that more than one rounding mode fits — such as when the sum of the floating-point values itself gives an integer — then the method is free to choose one of the rounding modes.

1.2. Huge and very precise numbers

The classes java.math.BigInteger and java.math.BigDecimal can be used to represent arbitrarily large integer and floating-point numbers.

1.2.1. Calculate arithmetic mean of a large integer ⭐

To calculate the arithmetic mean of two numbers, they are added together and divided by two. This works well if the sum of the two values does not exceed the largest number that can be represented. If there is an overflow, the result is wrong. Some algorithms in computer science can handle this problem as well, but we can make it a little easier and take the higher data type in each case. For example, if we want to calculate the mean value of two int variables, we convert the two int to a long, add the numbers, divide and convert back to the int. With the long data type, there is no larger primitive data type, but the BigInteger type can be used well for the case.

Task:

  • Calculate the arithmetic mean of two long values so that there is no overflow and wrong results. The result should be a long again.

1.2.2. Number by number over the phone ⭐

A new deal has made Bonny Brain a lot of money. The number is so big that you can’t just announce it over the phone at all; it has to be conveyed in chunks.

Number by number over the phone

Task:

  • Write a new method BigInteger completeNumber(int... parts) that gets a variable number of numbers and returns the big total number at the end.

Example:

  • completeNumber(123, 22, 989, 77, 9) returns a BigInteger with the value 12322989779.

1.2.3. Develop class for fractions and truncate fractions ⭐⭐

Bonny Brain is trying a new recipe for a rum punch. Fractions like "1/4 liter grape juice" or "1/2 liter rum" keep appearing in the directions for making it. For the party, she prepares 100 servings, and fractions like "100/4 liters" occur. The fractions can be shortened so that Bonny Brain knows, for example, that 25 liters of grape juice must be purchased.

Task:

  1. Create a new class Fraction.

  2. Let there be a constructor Fraction(int numerator, int denominator) that stores numerator and denominator in public final variables.

    • Consider whether there can be faulty arguments, which we should report through exceptions.

    • Every created fraction should be simplified automatically. Use the gcd(…​) method of BigInteger, which calculates the greatest common divisor (gcd)_. Remember: The gcd of numerator and denominator is the largest number by which both are divisible. You can simplify the fraction by dividing both the numerator and denominator by this number.

    • Both numerator and denominator can be negative, but then you can flip the sign so that both values become positive.

    • The objects are all supposed to be immutable, and therefore the variables can be public since they are not supposed to be changed after initialization via the constructor. In other words, the class Fraction does not contain any setters.

  3. Add the constructor Fraction(int value), where the denominator automatically becomes 1.

  4. Implement a method to multiply fractions and detect overflows.

  5. Implement a method reciprocal() that returns the inverse of a fraction, that is, swaps the numerator and denominator. Thanks to this method, the division of fractions can be implemented.

  6. Fraction shall extend java.lang.Number and implement all mandatory methods.

  7. Fraction shall implement Comparable because fractions can be converted to a decimal number, and decimal numbers have a natural order.

  8. Fraction shall implement equals(…​) and hashCode() correctly.

  9. Implement a toString() method that returns a string as bare as possible.

Fraction UML
Figure 1. UML diagram of Fraction.