Sunday, March 13, 2016

Intro To Java: Section 3.1 - BigDecimals

In this section you will learn how to create and use BigDecimals to store currency values and control rounding.

BigDecimals

Unfortunately none of the primitive data types are apropriate for storing currency. Integers and longs cannot hold decimals. Floats and doubles are not percise enough to represent currency. If we used floats and doubles to perform calculations in important financial systems the values could regularly be off by one penny or more. Balances would be off, customers would be angry, and accounting would be impossible. This is where the BigDecimal object comes into play - BigDecimals are specialized objects which are appropriate for storing currency values. BigDecimals are immutable objects meaning once you create it its value cannot be changed directly - in order to perform computations you must use the appropriate BigDecimal methods and create entirely new BigDecimals. In order to access this class in your program, it must be properly imported in the same manner as the Scanner object from the previous topic. In order to import and use Big Decimals, you must access it from the math package of the Java API.

import java.math.BigDecimal;
Once you've imported the BigDecimal class you will be able to use BigDecimals in your code. Keep in mind that BigDecimals are not the same as primitive data types. Like Scanner objects, BigDecimals are also treated as objects and must be initialized as follows. First you must tell Java what type of object you are creating (BigDecimal) and name it (myDecimal). You must then initialize it as a new BigDecimal object with a value of your choice. Pass the value you would like to set it to as the first argument. (I chose 250.05).

BigDecimal myDecimal = new BigDecimal("250.05");
Now that the BigDecimal is initialized we can use it elsewhere in our code. Just like primitive data types, we can add, subtract, divide, and multiply. Unfortunately we cannot simply use the traditional operators (+, -, /, *). We must use the methods provided to us by the BigDecimal class. View the following example.

//Create Big Decimals
 //Note - We are passing the argument as a string instead of a double (note the quotes)
 //       the quotation marks are optional here but this prevents rounding errors.
 BigDecimal decimalOne = new BigDecimal("10.00");
 BigDecimal decimalTwo = new BigDecimal("5.00");
 
 //Operation Examples using BigDecimal methods
 //Addition decimalAdditionResult = decimalOne + decimalTwo
 BigDecimal decimalAdditionResult = decimalOne.add(decimalTwo);
 
 //Subtraction decimalSubtractionResult = decimalOne - decimalTwo
 BigDecimal decimalSubtractionResult = decimalOne.subtract(decimalTwo);
 
 //Multiplication decimalProductResult = decimalOne * decimalTwo
 BigDecimal decimalProductResult = decimalOne.multiply(decimalTwo);
 
 //Division decimalDivisionResult = decimalOne / decimalTwo
 BigDecimal decimalDivisionResult = decimalOne.divide(decimalTwo);
The code above is fairly simple to understand. All we're doing is applying operations to decimalOne using whatever values are passed in the arguments - the value is then stored in the new respective result BigDecimal. Whenever you use these operations (add/subtract/multiple/divide) you are generating a new BigDecimal.
Although BigDecimals are immutable objects, it is possible to "change" their values indirectly by updating their reference variable. Technically, you are not changing the value of the BigDecimal, but instead are creating an entirely new BigDecimal object to which the old variable name references. For example, you could add a second BigDecimal to a first BigDecimal using the .add() method - this returns an entirely new BigDecimal to which you can reference using the old name. This works because we are not directly modifying the values of the original BigDecimal, but are replacing it with a new BigDecimal that has the same name.

import java.math.BigDecimal;
public class Topic03Demos {
 public static void main(String[] args) {
  
  //Create BigDecimals
  BigDecimal someDecimal = new BigDecimal("14.00");
  BigDecimal otherDecimal = new BigDecimal("6.00");
  
  //Add otherDecimal to someDecimal
  someDecimal = someDecimal.add(otherDecimal);
  
  //Print
  System.out.println("The value of someDecimal is " + someDecimal.toString()); 
  
 }
}
The value of someDecimal is 20.00
Note that if you are doing this, you must always do it in this format: "someDecimal = someDecimal.add(otherDecimal);" - you cannot just have "someDecimal.add(otherDecimal);"
The diagram below illustrates what happens in the previous example.
Given this knowledge, it is also possible to change the value of the BigDecimal by having it reference a new BigDecimal.

import java.math.BigDecimal;
public class Topic03Demos {
 public static void main(String[] args) {
  
  //Create BigDecimals
  BigDecimal aDecimal = new BigDecimal("14.00");
  
  System.out.println("The value of aDecimal is: " + aDecimal.toString());
  
  //Reference a new BigDecimal
  aDecimal = new BigDecimal("10.00");
  System.out.println("The value of aDecimal is: " + aDecimal.toString());
 }
}
Output
The value of aDecimal is: 14.00
The value of aDecimal is: 10.00

Printing Results

You may print out the results in the same manner that we printed out the values of variables in the previous sections. Note that Java is actually converting the value of the BigDecimal into a string automatically. This means we don't have to manually convert the BigDecimal into another primitive data type such as a double before we print out its result.

 //Printing out results - Java automatically converts BigDecimals to Strings for output
  System.out.println("Addition result is: " + decimalAdditionResult);
  System.out.println("Subtraction result is: " + decimalSubtractionResult);
  System.out.println("Product result is: " + decimalProductResult);
  System.out.println("Divison result is: " + decimalDivisionResult);
Addition result is: 15.00
Subtraction result is: 5.00
Product result is: 50.0000
Divison result is: 2
It is also possible to manually convert the BigDecimal to a string and then print out its value. Note that the two examples below are basically the same thing - we're just printing out two concatenated strings.

//Convert to String - option one
  String resultString = decimalAdditionResult.toString();
  System.out.println("Result is: " + resultString);
  
  //All in one step - option two
  System.out.println("Result is: " + decimalAdditionResult.toString());
Result is: 15
Result is: 15
Alternatively, you could manually convert the BigDecimals to doubles and then print the values out to whatever amount of spaces you want using the proper format specifiers. BigDecimals can be converted to doubles using the .doubleValue method.

//Convert Result to Double
  double additionResult = decimalAdditionResult.doubleValue();
  System.out.printf("The addition result to two decimal places is %.2f",additionResult);
The addition result to two decimal places is 15.00
Just like the string result of the BigDecimal can be printed using one line of code - we can also print out the double result for the BigDecimal using one line of code by throwing the BigDecimal.doubleValue directly into the print method's arguments.

System.out.printf("The addition result to two decimal places is %.2f",decimalAdditionResult.doubleValue());
You may use any of the above methods to print out the values of variables. Many other objects in Java aside from BigDecimal also make use of the .toString and .doubleValue methods.

Setting Scale To Control Rounding

In addition to being able to control the amount of decimal places in BigDecimals by converting them to doubles you can also use the BigDecimal setScale method. This is the preferred way to do it because it gives you control over exactly how your decimals will be rounded. After creating your BigDecimal, you can use the setScale method to define the amount of decimal places you would like the BigDecimal to go to. In most cases with currency you will be using a value of 2 to go to two decimal places. The format for creating a BigDecimal with a set scale is as follows:

BigDecimal someDecimal = new BigDecimal("value").setScale(scale, roundingMode);
The beginning of the BigDecimal is set in the same manner as we previously created BigDecimals. Now however, we have introduced the .setScale method. You should always pass this two arguments. First, you should pass it an integer which represents how many decimal places you want to use. Second, you need to select a rounding mode. There are five main rounding modes we will discuss. Additional modes exist and can be referenced in the Java API.
  • BigDecimal.ROUND_CEILING: Rounds to the next highest number (1.222 -> 1.23) (-1.222 -> -1.22)
  • BigDecimal.ROUND_DOWN: Rounds in the direction of zero (1.222 -> 1.22) (-1.222 -> -1.22)
  • BigDecimal.ROUND_FLOOR: Rounds to the next lowest number (1.222 -> 1.22) (-1.222 -> -1.23)
  • BigDecimal.ROUND_HALF_UP: Rounds up if the decimal is >= 5 (1.225 -> 1.23) (1.224 -> 1.22)
  • BigDecimal.ROUND_HALF_DOWN: Rounds down if the decimal is <= 5 (1.225 -> 1.22) (1.226 -> 1.23)
Typically in math class we're taught to round up at 0.5 and down at 0.4 - so this fits with the BigDecimal.ROUND_HALF_UP rounding mode. The example below demonstrates this type of rounding.

import java.math.BigDecimal;
public class Topic03Demos {
 public static void main(String[] args) {
  
  //Create a BigDecimal
  BigDecimal someDecimal = new BigDecimal("14.555").setScale(2, BigDecimal.ROUND_HALF_UP);
  
  //Print
  System.out.println("The value of someDecimal is " + someDecimal.toString()); 
  
 }
}
Output:
The value of someDecimal is 14.56
The output was 14.56 because 14.555 was rounded up to 14.56.

Unreferenced Objects With BigDecimals

In Java, it is possible to create unreferenced objects, which are not referenced by name. This is especially useful in situations where you want to create an object for a single purpose and then get rid of it. The example below illustrates how we would typically add 10 to a BigDecimal object - by creating a second BigDecimal which holds the values we want to add and then combining the two BigDecimals and storing them in a third BigDecimal which stores the total sum.

import java.math.BigDecimal;
public class Test {
 public static void main(String[] args) {
  //Adding 5 to aBigDecimal
  //Create aBigDecimal
  BigDecimal aBigDecimal = new BigDecimal("5");
  BigDecimal bBigDecimal = new BigDecimal("10");
  //Add the two together and store result in sumDecimal
  BigDecimal sumDecimal = aBigDecimal.add(bBigDecimal);
  //Print Results
  System.out.println("aBigDecimal + 10 = " + sumDecimal.toString());   
 }
}
aBigDecimal + 10 = 15
But what if you want to add 10 to aBigDecimal without creating bBigDecimal to store the value of 10? If you're only using the value 10 one time to perform one computation it's quite tiresome to create a second bBigDecimal object. This can be simplified by using an unreferenced object. You can create unreferenced objects without names directly in the arguments for the BigDecimal arithmetic methods.

import java.math.BigDecimal;
public class Test {
 public static void main(String[] args) {
  //Adding 5 to aBigDecimal
  //Create aBigDecimal
  BigDecimal aBigDecimal = new BigDecimal("5");
  //Add 10 to aBigDecimal and store the result in sumDecimal
  BigDecimal sumDecimal = aBigDecimal.add(new BigDecimal("10"));
  //Print Results
  System.out.println("aBigDecimal + 10 = " + sumDecimal.toString());   
 }
}
aBigDecimal + 10 = 15
The result is the same as in the previous example, but the method is different. We added "(new BigDecimal("10")" directly inside the arguments for the BigDecimal addition method. This created an unreferenced BigDecimal object with the value of 10 so we didn't have to create a second bBigDecimal to perform the one time computation.

Scanners With BigDecimals

It is important to understand how to get user input and store it as a BigDecimal. The method works quite similarly to how it did in the previous lesson. The example below demonstrates how you can ask a user to give you a monetary value and then print that value to the screen.

import java.math.BigDecimal;
import java.util.Scanner;
public class Topic03Demos {
 public static void main(String[] args) {
  
  //Create a scanner
  Scanner userInput = new Scanner(System.in);
  
  //Ask the user for a value
  System.out.println("Enter some monetary value");
  BigDecimal numberEntered = userInput.nextBigDecimal();
  
  //Close Scanner
  userInput.close();
  //Print Result
  System.out.println("The value of numberEntered is " + numberEntered.toString());
 }
}
Output:
Enter some monetary value
20.20
The value of numberEntered is 20.20

Review Exercise 3.1: Sales Tax

Instructions:Create a simple program using BigDecimals that calculates a sales tax of 6% and prints out the total.
Suggested Methodology:
  1. Create a Scanner to retrieve user input as a BigDecimal
  2. Create a BigDecimal with value 0.06 for multiplying with total to calculate taxes
  3. Create an additional taxes BigDecimal and total BigDecimal to store the tax amount and the total.
  4. Use the .setScale method to set the rounding scale to 2 on each of the BigDecimals.
  5. Use the BigDecimal methods to calculate the taxes and totals
  6. Print out the results
Sample Output
Please enter item price:
100.00
Item Price: $100.00
Sales Tax: $6.00
Total Price: $106.00
View Solution

0 comments:

Post a Comment