Primitive type
Size in memory
Value range
byte
8 bit
-128 to 127
short
16 bit
-32768 to 32767
char
16 bit
0 to 65536
int
32 bits
-2147483648 to 2147483647
long
64 bit
-9223372036854775808 to 9223372036854775807
float
32 bits
(2 to the power of -149) to ((2 to the power of -23) * 2 to the power of 127)
double
64 bit
(-2 to the power of 63) to ((2 to the power of 63) – 1)
boolean
8 (when used in arrays), 32 (when not used in arrays)
true or false
The roomiest integer data type is the
long
. When it comes to floating-point numbers, it’s the
double
.
But what if the number we need is so large that it does not even fit into a
long
?
The Long data type has a quite large range of possible values, but it is still limited to 64 bits.
What do we need to come up with if our Very Large Number requires 100 bits?
Fortunately, we don’t need to invent anything. For cases such as this, Java has two special classes:
BigInteger
(for integers) and
BigDecimal
(for floating-point numbers).
What makes them special?
First of all, in theory, they have no maximum size. We say “in theory”, because there are no computers with infinite memory. And if your program creates a number larger than the amount of available memory, then, the program will not work, of course. But such cases are unlikely.
As a result, we can say that
BigInteger
and
BigDecimal
can represent numbers of virtually unlimited size.
What are these classes used for?
First of all, for calculations with extremely rigorous accuracy requirements. For example, human life may depend on the accuracy of calculations in some programs (e.g. software that controls airplanes, rockets, or medical equipment).
So if the 150th decimal place is important, then
BigDecimal
is the best choice.
In addition, objects of this class are often used in the world of finance, where accurate calculation of even the smallest values is also extremely important.
How do you work with
BigInteger
and
BigDecimal
objects and do you need to know about them?
Objects of these classes are created like this:
public class Main {
public static void main(String[] args) {
BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
System.out.println(integer);
BigDecimal decimal = new BigDecimal("123.444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444");
System.out.println(decimal);
}
}
Passing a string to the constructor is just one possible option.
Here we use strings, because our numbers exceed the maximum values for
long
and
double
, and we do need some way to explain to the compiler which number we want to create 🙂
Simply passing the number
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
to the constructor won’t work: Java will try to cram the passed number into one of the primitive data types, but it won’t fit into any of them.
That’s why using a string to pass the desired number is a good option. Both classes can automatically extract numerical values from the passed strings.
Another important point to remember when working with big-number classes is that their objects are immutable (
Immutable
).
You’re already familiar with immutability thanks to your experience with the
String
class and the wrapper classes for primitive types (Integer, Long, etc.).
import java.math.BigInteger;
public class Main {
public static void main(String[] args) {
BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
System.out.println(integer);
integer.add(BigInteger.valueOf(33333333));
System.out.println(integer);
}
}
Console output:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
As you would expect, our number has not changed. To perform the addition operation, you must create a new object to receive the result of the operation.
import java.math.BigInteger;
public class Main {
public static void main(String[] args) {
BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
System.out.println(integer);
BigInteger result = integer.add(BigInteger.valueOf(33333333));
System.out.println(result);
}
}
Console output:
11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111144444444
See, now everything works as it should 🙂
By the way, did you notice how unusual the addition operation looks?
BigInteger result = integer.add(BigInteger.valueOf(33333333));
This is another important point. Big-number classes don’t use the + – * / operators. Instead, they provide a set of methods. Let’s get acquainted with the main ones (as always, you can find a complete list of methods in the Oracle documentation:
-
methods for arithmetic operations: add(), subtract(), multiply(), divide(). These methods are used to perform addition, subtraction, multiplication and division, respectively.
-
doubleValue(), intValue(), floatValue(), longValue(), etc. are used to convert a big number to one of Java’s primitive types. Be careful when using these methods. Don’t forget about the differences in bit size!
import java.math.BigInteger; public class Main { public static void main(String[] args) { BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); long result = integer.longValue(); System.out.println(result); } }
Console output:
8198552921648689607
-
min() and max() let you find the minimum and maximum value of two big numbers.
Note that these methods are not static!import java.math.BigInteger; public class Main { public static void main(String[] args) { BigInteger integer = new BigInteger("11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); BigInteger integer2 = new BigInteger("222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222"); System.out.println(integer.max(integer2)); } }
Console output:
222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
Tóm Tắt
BigDecimal rounding behavior
This topic has its own separate section, since rounding big numbers and configuring rounding behavior are not so simple.
You can use the
setScale()
method to set the number of decimal places for a
BigDecimal
.
For example, suppose we want the number
111.5555555555
to have three digits after the decimal point.
However, we can’t achieve what we want by passing the number 3 as an argument to the
setScale()
method.
As mentioned above,
BigDecimal
is for representing numbers with strict requirements on computational precision.
In its current form, our number has 10 digits after the decimal point. We want to drop 7 of them and keep only 3. Accordingly, in addition to the number 3, we must pass the rounding mode.
BigDecimal
has a total of 8 rounding modes. That’s a lot! But if you really need to fine tune the precision of your calculations, you’ll have everything you need. So, here are the 8 rounding modes offered by
BigDecimal
:
-
ROUND_CEILING — rounds up
111.5555555555 -> setScale(3, ROUND_CEILING) -> 111.556
-
ROUND_DOWN — rounds towards zero
111.5555555555 -> setScale(3, ROUND_DOWN) -> 111.555
-
ROUND_FLOOR — rounds down
111.5555555555 -> setScale(3, ROUND_FLOOR) -> 111.555
-
ROUND_HALF_UP — rounds up if the number after the decimal point >= 0.5
0.55 -> setScale(1, ROUND_HALF_UP) -> 0.6 0.54 -> setScale(1, ROUND_HALF_UP) -> 0.5
-
ROUND_HALF_DOWN — rounds up if the number after the decimal point > 0.5
0.55 -> setScale(1, ROUND_HALF_DOWN) -> 0.5 0.56 -> setScale(1, ROUND_HALF_DOWN) -> 0.6
-
ROUND_HALF_EVEN — rounding depends on the number to the left of the decimal point. If the number to the left is even, rounding will be down. If the number to the left of the decimal point is odd, then rounding will be up.
2.5 -> setScale(0, ROUND_HALF_EVEN) -> 2
The number to the left of the decimal place is 2 (even). The number is rounded down. We want 0 decimal places, so the result is 2.
3.5 -> setScale(0, ROUND_HALF_EVEN) -> 4
The number to the left of the decimal point is 3 (odd). The number is rounded up. We want 0 decimal places, so the result is 4.
-
ROUND_UNNECCESSARY — This mode is used when you must pass a rounding mode to a method, but the number does not need to be rounded. If you try to round a number with the ROUND_UNNECCESSARY mode set, an ArithmeticException is thrown.
3.51 -> setScale(1, ROUND_UNNECCESSARY) -> ArithmeticException
-
ROUND_UP — rounds away from zero.
111.5551 -> setScale(3, ROUND_UP) -> 111.556
Comparing big numbers
This is also important.
You will recall that we use the
equals()
method is compare objects in Java. The implementation is either provided by the language itself (for standard Java classes) or overriden by the programmer.
But in the case of
BigDecimal
objects, using the
equals()
method for comparisons is not recommended.
This is because the
BigDecimal.equals()
method returns true only if the 2 numbers have the same value and scale:
Let’s compare the behavior of the
equals()
method for the
Double
and
BigDecimal
classes:
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
Double a = 1.5;
Double b = 1.50;
System.out.println(a.equals(b));
BigDecimal x = new BigDecimal("1.5");
BigDecimal y = new BigDecimal("1.50");
System.out.println(x.equals(y));
}
}
Console output:
true
false
As you can see, for
BigDecimal
, the numbers 1.5 and 1.50 turned out to be unequal! This was precisely because of the specifics of the implementation of the
equals()
method in the
BigDecimal
class.
For a more accurate comparison of two
BigDecimal
objects, it is better to use the
compareTo()
method:
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal x = new BigDecimal("1.5");
BigDecimal y = new BigDecimal("1.50");
System.out.println(x.compareTo(y));
}
}
Console output:
0
The
compareTo()
method returned 0, which means that 1.5 and 1.50 are equal. And this is the result we expected! 🙂 That concludes our lesson today. Now it’s time to get back to the tasks! 🙂
Hi! In today’s lesson, we’ll talk about large numbers. No, I mean REALLY BIG. We have previously repeatedly encountered the table of value ranges for primitive data types. It looks like this:The roomiest integer data type is the. When it comes to floating-point numbers, it’s the. But what if the number we need is so large that it does not even fit into a? The Long data type has a quite large range of possible values, but it is still limited to 64 bits. What do we need to come up with if our Very Large Number requires 100 bits? Fortunately, we don’t need to invent anything. For cases such as this, Java has two special classes:(for integers) and(for floating-point numbers). What makes them special? First of all, in theory, they have no maximum size. We say “in theory”, because there are no computers with infinite memory. And if your program creates a number larger than the amount of available memory, then, the program will not work, of course. But such cases are unlikely. As a result, we can say thatandcan represent numbers of virtually unlimited size. What are these classes used for? First of all, for calculations with extremely rigorous accuracy requirements. For example, human life may depend on the accuracy of calculations in some programs (e.g. software that controls airplanes, rockets, or medical equipment). So if the 150th decimal place is important, thenis the best choice. In addition, objects of this class are often used in the world of finance, where accurate calculation of even the smallest values is also extremely important. How do you work withandobjects and do you need to know about them? Objects of these classes are created like this:Passing a string to the constructor is just one possible option. Here we use strings, because our numbers exceed the maximum values forand, and we do need some way to explain to the compiler which number we want to create 🙂 Simply passing the numberto the constructor won’t work: Java will try to cram the passed number into one of the primitive data types, but it won’t fit into any of them. That’s why using a string to pass the desired number is a good option. Both classes can automatically extract numerical values from the passed strings. Another important point to remember when working with big-number classes is that their objects are immutable (). You’re already familiar with immutability thanks to your experience with theclass and the wrapper classes for primitive types (Integer, Long, etc.).Console output:As you would expect, our number has not changed. To perform the addition operation, you must create a new object to receive the result of the operation.Console output:See, now everything works as it should 🙂 By the way, did you notice how unusual the addition operation looks?This is another important point. Big-number classes don’t use the + – * / operators. Instead, they provide a set of methods. Let’s get acquainted with the main ones (as always, you can find a complete list of methods in the Oracle documentation: here and here ).This topic has its own separate section, since rounding big numbers and configuring rounding behavior are not so simple. You can use themethod to set the number of decimal places for a. For example, suppose we want the numberto have three digits after the decimal point. However, we can’t achieve what we want by passing the number 3 as an argument to themethod. As mentioned above,is for representing numbers with strict requirements on computational precision. In its current form, our number has 10 digits after the decimal point. We want to drop 7 of them and keep only 3. Accordingly, in addition to the number 3, we must pass the rounding mode.has a total of 8 rounding modes. That’s a lot! But if you really need to fine tune the precision of your calculations, you’ll have everything you need. So, here are the 8 rounding modes offered byThis is also important. You will recall that we use themethod is compare objects in Java. The implementation is either provided by the language itself (for standard Java classes) or overriden by the programmer. But in the case ofobjects, using themethod for comparisons is not recommended. This is because themethod returns true only if the 2 numbers have the same value and scale: Let’s compare the behavior of themethod for theandclasses:Console output:As you can see, for, the numbers 1.5 and 1.50 turned out to be unequal! This was precisely because of the specifics of the implementation of themethod in theclass. For a more accurate comparison of twoobjects, it is better to use themethod:Console output:Themethod returned 0, which means that 1.5 and 1.50 are equal. And this is the result we expected! 🙂 That concludes our lesson today. Now it’s time to get back to the tasks! 🙂