Number too big for BigInteger - java

I'm developing a chemistry app, and I need to include the Avogadro's number:
(602200000000000000000000)
I don't really know if I can use scientific notation to represent it as 6.022 x 10x23 (can't put the exponent).
I first used double, then long and now, I used java.math.BigInteger.
But it still says it's too big, what can I do or should this is just to much for a system?

Pass it to the BigInteger constructor as a String, and it works just fine.
BigInteger a = new BigInteger("602200000000000000000000");
a = a.multiply(new BigInteger("2"));
System.out.println(a);
Output: 1204400000000000000000000

First of all, you need to check your physics / chemistry text book.
Avogadro's number is not 602,200,000,000,000,000,000,000. It is approximately 6.022 x 1023. The key word is "approximately". As of 2019, the precise value is 6.02214076×1023 mol−1
(In 2015 when I originally wrote this reply, the current best approximation for Avogadro's number was 6.022140857(74)×1023 mol−1, and the relative error was +/- 1.2×10–8. In 2019, the SI redefined the mole / Avogadro's number to be the precise value above. Source: Wikipedia)
My original (2015) answer was that since the number only needed 8 decimal digits precision, the Java double type was an appropriate type to represent it. Hence, I recommended:
final double AVOGADROS_CONSTANT = 6.02214076E23;
Clearly, neither int or long can represent this number. A float could, but not with enough precision (assuming we use the best available measured value).
Now (post 2019) the BigInteger is the simplest correct representation.
Now to your apparent problems with declaring the constant as (variously) an double, a long and a BigInteger.
I expect you did something like this:
double a = 602200000000000000000000;
and so on. That isn't going to work, but the reason it won't work needs to be explained. The problem is that the number is being supplied as an int literal. An int cannot be that big. The largest possible int value is 231 - 1 ... which is a little bit bigger than 2 x 109.
That is what the Java compiler was complaining about. The literal is too big to be an int.
It is too big for long literal as well. (Do the math.)
But it is not too big for a double literal ... provided that you write it correctly.
The solution using BigInteger(String) works because it side-steps the problem of representing the number as a numeric literal by using a string instead, and parsing it at runtime. That's OK from the perspective of the language, but (IMO) wrong because the extra precision is an illusion.

You can use E notation to write the scientific notation:
double a = 6.022e23;

The problem is with how you're trying to create it (most likely), not because it can't fit.
If you have just a number literal in your code (even if you try to assign it to a double or long), this is first treated as an integer (before being converted to the type it needs to be), and the number you have can't fit into an integer.
// Even though this number can fit into a long, it won't compile, because it's first treated
// as an integer.
long l = 123456788901234;
To create a long, you can add L to your number, so 602200000000000000000000L, although it won't fit into a long either - the max value is 263-1.
To create a double, you can add .0 to your number, so 602200000000000000000000.0 (or 6.022e23 as Guffa suggested), although you should not use this if you want precise values, as you may lose some accuracy because of the way it stores the value.
To create a BigInteger, you can use the constructor taking a string parameter:
new BigInteger("602200000000000000000000");

Most probably you are using long to initialize BigInteger. Since long can represent 64-bit numbers, your number would be too big to fit in to long. Using String would help.

Related

Java convert/cast object to Double but prevent round?

Object num = 12334555578912349.13;
System.out.println(BigDecimal.valueOf(((Number) num).doubleValue()).setScale(2, BigDecimal.ROUND_HALF_EVEN));
I expect the value to be 12334555578912349.13
but output is 123345555789123504.00
How can I prevent it from rounding?
Object num = 12334555578912349.13;
You already lost here. As per the java spec, the literal text '12334555578912349.13' in your source file is interpreted as a double. A double value, given that computers aren't magic, is a 64-bit value, and thus, only at most 2^64 numbers are even representable by it. Let's call these the 'blessed numbers'. 12334555578912349.13 is not one of the blessed numbers. The nearest blessed number to that is 123345555789123504.0 which is what that value 'compiles' to. Once you're at 123345555789123504.0, there's no way back.
The solution then is to never have '12334555578912349.13' as literal in your source file, as that immediately loses you the game if you do that.
Here's how we avoid ever having a double anywhere:
var bd = new BigDecimal("12334555578912349.13");
System.out.println(bd);
In general if you want to provide meaningful guarantees about precision, if your code contains the word double anywhere in it, you broke it. Do a quick search through the code for the word 'double' and until that search returns 0 hits, keep eliminating its use.
Alternatives that can provide guarantees about precision:
BigDecimal, of course. Note that BD can't divide without specifying rules about how to round it (for the same reason 1/3 becomes 0.333333... never ends).
Eliminate the fraction. For example, if that represents the GDP of a nation, store it as cents and not as euros, in a long and not a double.
If the thing doesn't represent a number that you ever intend to do any math on (for example, it's a social security number or an ISBN code or some such), store it as a String.

Base 2 custom method and approximation in Java

I'm writing a method of a Network Calculator that calculates how many bits are needed giving a number of hosts. This is the piece of code that is giving me some problems:
int hostBits = (int) Math.ceil(Math.log(hosts) / Math.log(2));
It works fine with small numbers, but the problem comes up when I enter big ones, like 2^31 - 2, because the result of Math.log(hosts) / Math.log(2) (changing the base of the logarithm), that should be 31.0, actually is 31.000000000000004.
Then, Math.ceil(...) brings 31.000000000000004 to 32 and not to 31 as I want.
Does anyone have an idea how to solve this?
Not the problem.
The problem is only that when a number is divided , for example two different integers (int's) the result particularly with "Math" and log methods is "precision" that it expects may well be required to be some custom level or default level of a primitive type such as double - hence the extra decimal places after the decimal point.
Two points, first, when a number is divided that technically would simply be an integer e.g. 12 / 3 = 6 with math and as a primitive type double it will always have decimal point followed by around six zero's or so and a number other than 0 at the end. Second, it could appear worse from the Math library you could get logarithmic notations from a division not decimal places.
A final point, the Math library does not know what the answer will be until it does it so using double type is an automatic conversion no matter how you write it for safety of precision and data type.
Automatic swapping of data types is known as promotion (raising precision int to a float) or demotion (lowering precision double to a float)
And to convert it to an integer it will round up or down by rules of the first following decimal place from the point, so you basically only need to put the answer into a "new Double(primativedoubletype)" object and use its "intValue" method.
NB putting an (int) cast in front of it only ensures that it is an int when assigned (generally from an expression or method) to an int variable, however it is not a way of extracting the value of another type to an int, the primitive type must be converted through its standard java type class object with its primatves converter method.
int hostBits2 = new Double((double)Math.ceil(Math.log(hosts) / Math.log(2)+1e-10)).intValue();
Try using epsilon while rounding
int hostBits2 = (int) Math.ceil(Math.log(hosts) / Math.log(2)+1e-10);

Java Variable Numeric Literals

I am new in java. I have a problem with Numeric literal. Here is my problem:
float rank = 1050.86F;
System.out.println(rank);
The output is: 1050.86
double rank1 = 1050.86D;
double rank2 = 1050.86F;
System.out.println(rank1);
System.out.println(rank2);
The output of rank1 is: 1050.86
The output of rank2 is: 1050.8599853515625
My question is:
(i)Why the output of rank1 and rank2 are different? and how I calculate that?
(ii) Why do I need to use L, D, F before semicolon? As we already used keyword double, int, so why we need to use L, D, F on variables?
Please Help me. I am new in programming.
To answer your first question, it has to do with how computers store numbers internally. Computers use floating point to store numbers. Double precision ("double") stores more "information" in each number than floats. There are 64 bits stored for a double, and 32 bits stored for a float. See how 64 is twice the amount of bits as 32? That's why it's called a double!
Both doubles and floats can only store a limited amount of information in their types, though, so they're not perfectly precise and you can expect little inaccuracies like that to happen all the time in programming. It's nothing to worry about, in fact it's very common to run into floating point errors like you describe. There is no solution other than you should use doubles if you want more precise decimal values. Explaining how to deal with floating point error would take a lot of explaining, so I will link one of my favorite explanations of it here.
To answer your second question, if you don't specify, Java will assume you're using a double value (see here), so you have to specify that you want a float instead. This is because most often you will want to use a double (like I said above, it stores more information), so it makes sense that double is default and you have to specify if you want a float instead.

How accurate is "double-precision floating-point format"?

Let's say, using java, I type
double number;
If I need to use very big or very small values, how accurate can they be?
I tried to read how doubles and floats work, but I don't really get it.
For my term project in intro to programming, I might need to use different numbers with big ranges of value (many orders of magnitude).
Let's say I create a while loop,
while (number[i-1] - number[i] > ERROR) {
//does stuff
}
Does the limitation of ERROR depend on the size of number[i]? If so, how can I determine how small can ERROR be in order to quit the loop?
I know my teacher explained it at some point, but I can't seem to find it in my notes.
Does the limitation of ERROR depend on the size of number[i]?
Yes.
If so, how can I determine how small can ERROR be in order to quit the loop?
You can get the "next largest" double using Math.nextUp (or the "next smallest" using Math.nextDown), e.g.
double nextLargest = Math.nextUp(number[i-1]);
double difference = nextLargest - number[i-1];
As Radiodef points out, you can also get the difference directly using Math.ulp:
double difference = Math.ulp(number[i-1]);
(but I don't think there's an equivalent method for "next smallest")
If you don't tell us what you want to use it for, then we cannot answer anything more than what is standard knowledge: a double in java has about 16 significant digits, (that's digits of the decimal numbering system,) and the smallest possible value is 4.9 x 10-324. That's in all likelihood far higher precision than you will need.
The epsilon value (what you call "ERROR") in your question varies depending on your calculations, so there is no standard answer for it, but if you are using doubles for simple stuff as opposed to highly demanding scientific stuff, just use something like 1 x 10-9 and you will be fine.
Both the float and double primitive types are limited in terms of the amount of data they can store. However, if you want to know the maximum values of the two types, then run the code below with your favourite IDE.
System.out.println(Float.MAX_VALUE);
System.out.println(Double.MAX_VALUE);
double data type is a double-precision 64-bit IEEE 754 floating point (digits of precision could be between 15 to 17 decimal digits).
float data type is a single-precision 32-bit IEEE 754 floating point (digits of precision could be between 6 to 9 decimal digits).
After running the code above, if you're not satisfied with their ranges than I would recommend using BigDecimal as this type doesn't have a limit (rather your RAM is the limit).

Storing large decimal numbers in Java

I need to store 17774132 in a double format,
but it seems that double is to small since I get 1.7774132E7.
How can I overcome this problem? I need some kind of primitive that can hold it with floating point.
Thank you
In java if you want accurate calculations for large numbers with fractions, you should use java.math.BigDecimal class. The integer counterpart is java.math.BigInteger.
Also I think double can accomodate 17774132, it's just showing the value in something called as "E Notation" which a Scientific notation to denote numbers. Refer to this : http://en.wikipedia.org/wiki/Scientific_notation#E_notation
Remeber that means 1.7774132 * 10^7, so the value is represented by:
1.7774132 * 10000000
That's a big number, don't you think?
Java outputs by default on scientific notation if needed. Big numbers like that are expressed in scientific notation.
1.7774132E7 is exactly the same as 17774132. It's just displayed in scientific notation. A double is more than capable of holding this value. As others have said, though, use BigDecimal if you're worried about size or accuracy.
First, hopefully you recognize the issues with floating-point decimal representations.
17774132 is equivalent to 1.7774132E7; the "E7" means it is being multiplied by 10^7. If your issue is that you want it displayed differently, you can use a NumberFormat.
Note that 17774132 is actually an integer and well below the threshold of ~2.1 billion for the int primitive type. The long primitive type lets you go even higher if you are actually using integers.
If you want to represent the number differently and it is not an integer, you can try BigDecimal (or BigInteger for legitimate integers too big for a long).

Categories

Resources