Dealing with innaccuracy from double arithmetic? [duplicate] - java

This question already has answers here:
Large Numbers in Java
(6 answers)
Closed 5 years ago.
I've written a method that tests whether a number is prime or not. To maximise the range of numbers that the user can input, I wanted to use doubles. The issue is, after testing it on a very large prime, like 40 digits or so, my method returns false (I've already tested the logic with an int version, and it works just fine as far as I can tell).
Here is my code:
public static boolean isPrime(double number) {
double sqrt = Math.sqrt(number);
for(double i = 2; i<= sqrt; i++) {
if(number%i == 0) {
return false;
}
}
I know the reason it's not working at very high numbers is because of the accuracy error, but is there around this?

I know the reason it's not working at very high numbers is because of the accuracy error, but is there around this?
Yes. Use BigInteger.
Note that long would be better than double, since long can represent all integers up to 2^63 - 1 precisely. By contrast, with double you start losing precision at 2^53 + 1. However neither of these types are suitable for 40 (decimal) digit numbers.
BigInteger arithmetic is significantly slower, but the you will be able to go up to (at least) 2^Integer.MAX_VALUE ... provided that you have enough heap space.

Related

How to round a small negative double to not cause minus zero in Java [duplicate]

This question already has answers here:
How can a primitive float value be -0.0? What does that mean?
(5 answers)
Closed 2 years ago.
I need to round a small negative number to return 0.0. However, the value I'm getting is minus zero. The following code demonstrates the problem:
double value = -0.000000001;
double roundedValue = Double.valueOf(String.format(Locale.US, "%.4f", value));
System.out.println(roundedValue); // I need the roundedValue to be equal 0.0 (not -0.0)
There is a way to fix this?
You can explicitly handle the negative zero case:
roundedValue = (roundedValue == 0.0 && 1 / roundedValue < 0) ? 0 : roundedValue;
(1 / roundedValue < 0 to check for negative zero, from this answer)
Nit: use Double.parseInt rather than valueOf, to avoid the unnecessary boxing and immediate unboxing.
First, you have to realize that double values are semantically different from integers. They are imprecise, so there is always ever-so-slight error in every double value. The error, however, is small, but in unfortunate case it could be on the other side of zero - technically correct, but not what you want. This understanding is essential; if you need digit-exact arithmetic, you shouldn't be using doubles anyway, but integers/longs. Part of IEEE specification for double values also defines "negative zero", "NaN", "infinity" and so on, so technically the software is correct, but you are not using it the right way for what you want to achieve.
Second, like other people already mentioned, never use string formatting for rounding. If you need 4 decimal places, a much better way is to multiply the number by 10000, take floor/round of it and divide it by 10000 again. However, due to the facts mentioned above, you might again get some small decimal off (such as the 15th decimal digit).
On the other hand, if you just want to get rid of "rounding noise" which is sufficiently close to zero, you can also use this approach which is very robust:
if (Math.abs(x) < 0.000001d) x = 0d;
Much convenient would be:
double roundedValue = value < 0 ? (Math.ceil(value) == -0 ? 0 : Math.ceil(value)) : value;
In this case, you are going to preserve - values: for example -1.5 the result would be -1, but -0.00000001 is 0.0.

Maximum value of Java Biginteger in pow [duplicate]

This question already has answers here:
BigInteger.pow(BigInteger)?
(9 answers)
Closed 3 years ago.
I have code that will get the exponent value from given input:
BigInteger a= new BigInteger(2,10);
BigInteger b;
b=a.pow(9999999999);
It is working when value is lower than 7 digits. For example:
BigInteger a= new BigInteger(2,10);
BigInteger b;
b=a.pow(1234567);
Does my code make it possible or is it not possible to have 10 digit in the exponent?
I'm using JDK 1.8.
BigInteger.pow() only exists for int parameters, so you can't take the power bigger than Integer.MAX_VALUE at once.
Those numbers would also be incredibly big (as in "rapidly approaching and passing the number of particles in the observable universe), if you could do it, and there are very few uses for this.
Note that the "power with modulus" operation which is often used in cryptography is implemented using BigInteger.modPow() which does take BigInteger arguments and can therefore handle effectively arbitrarily large values.
pow's parameter is an int. The range of int is -2147483648 to 2147483647, so the answer is it depends on which 10 digits you use. If those 10 digits are 1234567890, that's fine, it's in range (though potentially you'll get a really, really big BigInteger which may push your memory limits); if they're 9999999999 as in your question, that's not fine, it's out of range.
E.g., this compiles:
int a = 1234567890;
This does not:
int b = 9999999999;
^--------------- error: integer number too large: 9999999999

Rounding of big Fibonacci numbers [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 5 years ago.
Folks, hope you could point me, what I missing out. I am new to programming and now solving problems for Project Euler. One of the tasks is to calculate the sum of even numbers of Fibonacci sequence. I wrote code which passed 4 of 5 test. Here it is:
//N is input (N<=4*10^16), let's find n by Binet's formula
double a = log10(sqrt (5) * N);
double b = log10((1 + sqrt (5)) / 2);
int n = (int) (a / b);
//Using same formula, we find every Fibonacci number till n
for (int i = 1; i <= n; i++){
a = (1 + sqrt(5)) / 2;
long fiboNum = Math.round (Math.pow(a,i) / sqrt(5));
And here is my problem: when I run a test with big numbers, there is some rounding happening around F(71).
By my code, F(71) is 308 061 521 170 130, according to WolphramAlpha calculations 308 061 521 170 129.
Because of this, final sum is wrong.
Also, while testing I printed out all numbers and recognize, that even numbers repeat every 3. But even if I simplify it to the small loop of sum every third number, still problem exist.
If I do not use Math.round, I am getting the wrong sequence...
double has a limited precision. Try using BigDecimal which has precision only limited by the size of available memory.
Here is how you compute square root with BigDecimals: Square root of BigDecimal in Java
pow(): Java's BigDecimal.power(BigDecimal exponent): Is there a Java library that does it?
Rounding: Java BigDecimal: Round to the nearest whole value
Other operations like addition and division are built-in into BigDecimal as methods.

integer giving negative values in java in multiplication using positive numbers [duplicate]

This question already has answers here:
Multiplication of two ints overflowing to result in a negative number
(5 answers)
Closed 9 years ago.
public class Test {
public static void main(String[] args) {
int sum=0;
for(int i=1;i<10;i++)
sum = sum+i*i*i*i*i*i*i*i*i*i;
System.out.println(sum);
}
}
OUTPUT:619374629
for(int i=1;i<10;i++)
sum = sum+i*i*i*i*i*i*i*i*i*i*i;
System.out.println(sum);
OUTPUT:
-585353335
In the second output i thought the integer range crossed.But why it is giving -ve number.It need to give me an error.What is the reason for this behaviour?
Thanks in advance...
You have overflowed the size of a 32 bit integer.
Consider what happens when i is equal to 10:
sum = sum + 100000000000 //1 with 11 zeroes
But the maximum positive number that can be stored in a 32 bit integer is only about 2 billion (2 with nine zeroes).
In fact, it gets even worse! The intermediate calculations will be performed with limited precision, and as soon as the multiplication of 10*10*10*10... overflows, then the 10s will be being multiplied with a weird negative number, and be already wrong.
So the number you end up with is not seeming to follow any rules of arithmetic, but in fact it makes perfect sense once you know that primitive integers have limited storage.
The solution is to use 64-bit long and hope you don't overflow THAT too, if you do then you need BigInteger.
Java defines integer math as signed 2s-complement mod 2^32 (for int) and 2^64 (for long). So any time that the result of an int multiply is 2^31 or higher, it wraps around and becomes a negative number. That's just the way integer math works in java.
Java spec
You are using the primitive type. So, when an integer overflows, it will only print out the bits contained in it which is negative. If you want error, try Integer.
As you predicted, you passed the integer range, though causing infinite values (as the sign [the highest bit] gets touched).

Getting factorial of a large number [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Calculate the factorial of an arbitrarily large number, showing all the digits
This question might be asked thousand times here. I am repeating my question with a modification. I want to calculate factorial of a large number (max range of the number=10^6). Generally we uses a for loop from i=1 to i=number and every time multiplying the old value to the new value. This is fine for small numbers but what if i have a large number? The range of for loop is increased now. Java primitive data type int, long can't handle the resultant large number. They simply overflows. Although i knew about BigInteger class, which can handle this large output but still for loop is not suiting better here for me. Can somebody please suggest me any tick, any hack to calculate factorial of a number? Below is the simple program which works fine for small number-
public long getFactorial(long number) {
long factorial = 1;
for (long i = 1; i <= number; ++i) {
factorial *= i;
}
return factorial;
}
Understand that the value is in the range of 105565708. It's going to take up about two megabytes of space, all by itself.
That said, Guava's BigIntegerMath.factorial(int) is good enough to handle it, and more importantly, it's actually been optimized for large factorials -- it'll do significantly better than a straightforward for loop. (Disclosure: I contribute to Guava...and wrote much of BigIntegerMath.factorial myself.)
That said, I wouldn't exactly call it fast -- my benchmarks indicate an average of 414ms for a factorial in that range -- but there isn't going to be a truly fast solution, not without extremely heavy-duty bignum libraries, and I wouldn't expect even those to be significantly faster.
That's if you need the exact value. If you can settle for the logarithm, then yeah, either use Apache's logGamma(n+1) to get ln(n!), or approximate it yourself:
double logFactorial = 0;
for (int i = 2; i <= n; i++) {
logFactorial += Math.log(i);
}
Some rounding error will probably accumulate, but it's supposed to be an approximation anyway.
You can use an approximation function called Stirling's Formula for such large values of n.
You can refer to my answer here for more details:
https://softwareengineering.stackexchange.com/questions/134968/number-of-combinations/134972#134972
Use the Gamma function. Gamma(i + 1) = i!
org.apache.commons.math.special provides it for Java.
What people typically do is to calculate log(Gamma(i+1)) and then work in log-space (multiplications become additions, etc.).
Here are some other methods to calculate the factorial quickly: http://www.luschny.de/math/factorial/FastFactorialFunctions.htm
You use BigInteger for factorial, and you still only need a long for i, the loop variable. i only has to go up to 1000000. What's the problem?

Categories

Resources