Fast calculation of RMS gives NaNs in Java - floating point error? - java

I'm getting a perplexing result doing math with floats. I have code that should never produce a negative number producing a negative number, which causes NaNs when I try to take the square root.
This code appears to work very well in tests. However, when operating on real-world (i.e. potentially very small, seven and eight negative exponents) numbers, eventually sum becomes negative, leading to the NaNs. In theory, the subtraction step only ever removes a number that has already been added to the sum; is this a floating-point error problem? Is there any way to fix it?
The code:
public static float[] getRmsFast(float[] data, int halfWindow) {
int n = data.length;
float[] result = new float[n];
float sum = 0.000000000f;
for (int i=0; i<2*halfWindow; i++) {
float d = data[i];
sum += d * d;
}
result[halfWindow] = calcRms(halfWindow, sum);
for (int i=halfWindow+1; i<n-halfWindow; i++) {
float oldValue = data[i-halfWindow-1];
float newValue = data[i+halfWindow-1];
sum -= (oldValue*oldValue);
sum += (newValue*newValue);
float rms = calcRms(halfWindow, sum);
result[i] = rms;
}
return result;
}
private static float calcRms(int halfWindow, float sum) {
return (float) Math.sqrt(sum / (2*halfWindow));
}
For some background:
I am trying to optimize a function that calculates a rolling root mean square (RMS) function on signal data. The optimization is pretty important; it's a hot-spot in our processing. The basic equation is simple - http://en.wikipedia.org/wiki/Root_mean_square - Sum the squares of the data over the window, divide the sum by the size of the window, then take the square.
The original code:
public static float[] getRms(float[] data, int halfWindow) {
int n = data.length;
float[] result = new float[n];
for (int i=halfWindow; i < n - halfWindow; i++) {
float sum = 0;
for (int j = -halfWindow; j < halfWindow; j++) {
sum += (data[i + j] * data[i + j]);
}
result[i] = calcRms(halfWindow, sum);
}
return result;
}
This code is slow because it reads the entire window from the array at each step, instead of taking advantage of the overlap in the windows. The intended optimization was to use that overlap, by removing the oldest value and adding the newest.
I've checked the array indices in the new version pretty carefully. It seems to be working as intended, but I could certainly be wrong in that area!
Update:
With our data, it was enough to change the type of sum to a double. Don't know why that didn't occur to me. But I left the negative check in. And FWIW, I was also able to implement a sol'n where recomputing the sum every 400 samples gave great run-time and enough accuracy. Thanks.

is this a floating-point error problem?
Yes it is. Due to rounding, you could well get negative values after subtracting a previous summand.
For example:
float sum = 0f;
sum += 1e10;
sum += 1e-10;
sum -= 1e10;
sum -= 1e-10;
System.out.println(sum);
On my machine, this prints
-1.0E-10
even though mathematically, the result is exactly zero.
This is the nature of floating point: 1e10f + 1e-10f gives exactly the same value as 1e10f.
As far as mitigation strategies go:
You could use double instead of float for enhanced precision.
From time to time, you could fully recompute the sum of squares to reduce the effect of rounding errors.
When the sum goes negative, you could either do a full recalculation as in (2) above, or simply set the sum to zero. The latter is safe since you know that you'll be pushing the sum towards its true value, and never away from it.

Try checking your indices in the second loop. The last value of i will be n-halfWindow-1 and n-halfWindow-1+halfWindow-1 is n-2.
You may need to change the loop to for (int i=halfWindow+1; i<n-halfWindow+1; i++).

You are running into issues with floating point numbers because you believe that they are just like mathematical real numbers. They are not, they are approximations of real numbers, mapped into discrete numbers, with a few special rules added into the mix.
Take the time to read up on what every programmer should know about floating point numbers, if you intend to use them often. Without some care the differences between floating point numbers and real numbers can come back and bite you in the worst ways.
Or, just take my word for it and know that every floating point number is "pretty close" to the requested value, with some being "dead on" accurate, but most being "mostly" accurate. This means you need to account for measurement error and keep it in mind after the calculations or risk believing you have an exact result at the end of the computation of the value (which you don't).

Related

Calculating sin function with JAVA BigDecimal -monomial is going bigger(?)

I'm making sin function with BigDecimal in JAVA, and this is as far as I go:
package taylorSeries;
import java.math.BigDecimal;
public class Sin {
private static final int cutOff = 20;
public static void main(String[] args) {
System.out.println(getSin(new BigDecimal(3.14159265358979323846264), 100));
}
public static BigDecimal getSin(BigDecimal x, int scale) {
BigDecimal sign = new BigDecimal("-1");
BigDecimal divisor = BigDecimal.ONE;
BigDecimal i = BigDecimal.ONE;
BigDecimal num = null;
BigDecimal result = x;
//System.err.println(x);
do {
x = x.abs().multiply(x.abs()).multiply(x).multiply(sign);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
num = x.divide(divisor, scale + cutOff, BigDecimal.ROUND_HALF_UP);
result = result.add(num);
//System.out.println("d : " + divisor);
//System.out.println(divisor.compareTo(x.abs()));
System.out.println(num.setScale(9, BigDecimal.ROUND_HALF_UP));
} while(num.abs().compareTo(new BigDecimal("0.1").pow(scale + cutOff)) > 0);
System.err.println(num);
System.err.println(new BigDecimal("0.1").pow(scale + cutOff));
return result.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
}
It uses Taylor series :
picture of the fomular
The monomial x is added every iteration and always negative number.
And the problem is, absolute value of x is getting bigger and bigger, so iteration never ends.
Is there and way to find them or better way to implement it from the first place?
EDIT:
I made this code from scratch with simple interest about trigonometric functions, and now I see lots of childish mistakes.
My intention first was like this:
num is x^(2k+1) / (2k+1)!
divisor is (2k+1)!
i is 2k+1
dividend is x^(2k+1)
So I update divisor and dividend with i and compute num by sign * dividend / divisor and add it to result by result = result.add(num)
so new and good-working code is:
package taylorSeries;
import java.math.BigDecimal;
import java.math.MathContext;
public class Sin {
private static final int cutOff = 20;
private static final BigDecimal PI = Pi.getPi(100);
public static void main(String[] args) {
System.out.println(getSin(Pi.getPi(100).multiply(new BigDecimal("1.5")), 100)); // Should be -1
}
public static BigDecimal getSin(final BigDecimal x, int scale) {
if (x.compareTo(PI.multiply(new BigDecimal(2))) > 0) return getSin(x.remainder(PI.multiply(new BigDecimal(2)), new MathContext(x.precision())), scale);
if (x.compareTo(PI) > 0) return getSin(x.subtract(PI), scale).multiply(new BigDecimal("-1"));
if (x.compareTo(PI.divide(new BigDecimal(2))) > 0) return getSin(PI.subtract(x), scale);
BigDecimal sign = new BigDecimal("-1");
BigDecimal divisor = BigDecimal.ONE;
BigDecimal i = BigDecimal.ONE;
BigDecimal num = null;
BigDecimal dividend = x;
BigDecimal result = dividend;
do {
dividend = dividend.multiply(x).multiply(x).multiply(sign);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
i = i.add(BigDecimal.ONE);
divisor = divisor.multiply(i);
num = dividend.divide(divisor, scale + cutOff, BigDecimal.ROUND_HALF_UP);
result = result.add(num);
} while(num.abs().compareTo(new BigDecimal("0.1").pow(scale + cutOff)) > 0);
return result.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
}
The new BigDecimal(double) constructor is not something you generally want to be using; the whole reason BigDecimal exists in the first place is that double is wonky: There are almost 2^64 unique values that a double can represent, but that's it - (almost) 2^64 distinct values, smeared out logarithmically, with about a quarter of all available numbers between 0 and 1, a quarter from 1 to infinity, and the other half the same but as negative numbers. 3.14159265358979323846264 is not one of the blessed numbers. Use the string constructor instead - just toss " symbols around it.
every loop, sign should switch, well, sign. You're not doing that.
In the first loop, you overwrite x with x = x.abs().multiply(x.abs()).multiply(x).multiply(sign);, so now the 'x' value is actually -x^3, and the original x value is gone. Next loop, you repeat this process, and thus you definitely are nowhere near the desired effect. The solution - don't overwrite x. You need x, throughout the calculation. Make it final (getSin(final BigDecimal x) to help yourself.
Make another BigDecimal value and call it accumulator or what not. It starts out as a copy of x.
Every loop, you multiply x to it twice then toggle the sign. That way, the first time in the loop the accumulator is -x^3. The second time, it is x^5. The third time it is -x^7, and so on.
There is more wrong, but at some point I'm just feeding you your homework on a golden spoon.
I strongly suggest you learn to debug. Debugging is simple! All you really do, is follow along with the computer. You calculate by hand and double check that what you get (be it the result of an expression, or whether a while loop loops or not), matches what the computer gets. Check by using a debugger, or if you don't know how to do that, learn, and if you don't want to, add a ton of System.out.println statements as debugging aids. There where your expectations mismatch what the computer is doing? You found a bug. Probably one of many.
Then consider splicing parts of your code up so you can more easily check the computer's work.
For example, here, num is supposed to reflect:
before first loop: x
first loop: x - x^3/3!
second loop: x - x^3/3! + x^5/5!
etcetera. But for debugging it'd be so much simpler if you have those parts separated out. You optimally want:
first loop: 3 separated concepts: -1, x^3, and 3!.
second loop: +1, x^5, and 5!.
That debugs so much simpler.
It also leads to cleaner code, generally, so I suggest you make these separate concepts as variables, describe them, write a loop and test that they are doing what you want (e.g. you use sysouts or a debugger to actually observe the power accumulator value hopping from x to x^3 to x^5 - this is easily checked), and finally put it all together.
This is a much better way to write code than to just 'write it all, run it, realize it doesn't work, shrug, raise an eyebrow, head over to stack overflow, and pray someone's crystal ball is having a good day and they see my question'.
The fact that the terms are all negative is not the problem (though you must make it alternate to get the correct series).
The term magnitude is x^(2k+1) / (2k+1)!. The numerator is indeed growing, but so is the denominator, and past k = x, the denominator starts to "win" and the series always converges.
Anyway, you should limit yourself to small xs, otherwise the computation will be extremely lengthy, with very large products.
For the computation of the sine, always begin by reducing the argument to the range [0,π]. Even better, if you jointly develop a cosine function, you can reduce to [0,π/2].

Find the modo value

I have given a log value Y , i want to calculate the anti log of Y i.e
ans = (Math.pow(10,Y))%mod
where mod = 1e9+7 and the anti log of Y will always be integer i.e Y is calculate as follow Y= log(a) a is very large integer of range 10^100000
So for given Y i need to calculate ans ? How to do that considering the mod operation.
My Approach
double D = Y -(int)Y
long Pow = (long)Y
for(int i=1;i<=Pow;i++) ans = (ans*10)%mod;
ans = (ans*Math.pow(10,D))%mod
But it's not correct can someone suggest be efficient approach here ? BigDecimal can be useful there ?
For Example:
Y = 16.222122660468525
Using the straight forward method and rounding off i.e Math.log(10,Y) give me 1667718169966651 but using loops it's give me 16677181699666510. I am not using mod now just explaining that there is an error.
Here Y is small so direct method works and we can take mod easily. if Y is range of 10000 it will not work and overflow so we have to used mod.
I guess it's should work
double D = Y -(int)Y
long Pow = (long)Y
for(int i=1;i<=Pow;i++) ans = (ans*10)%mod;
ans = (ans*Math.pow(10,D))
ans = Math.round(ans)
ans%=mod
There is an error in your judgement here - the loop method is not at fault.
The value of a in your example has 17 integer digits. From this stackoverflow post, a double has ~16 significant digits of precision. Thus both the loop and direct calculations are in fact being limited by lack of precision.
(Just to confirm, using a high precision calculator, the value of a is 16677181699666650.8689546562984070600381634077.... Thus both of your values are incorrect - unless you copied them wrongly?)
Thus your loop method is not the problem; you just need a
higher-precision method to do the last step (calculating pow(10, frac(Y))).
As a side note, there is a more efficient way of doing the loop part - this post has more details.

Concurrently adding a value in loop

I am trying to add a value 1.12 between the Min and Max values
Min = 1.3
Max = 6.9
ie
1.3 + 1.12 = 2.42
2.42 + 1.12 = 3.54
till it reaches max value.
What I did is
double sum = 0,BucWidth = 1.12;
sum = min + BucWidth ;
while(sum != max){
sum = sum +BucWidth ;
System.out.println("SUmmmmm" + sum);
}
But it is not stopping when sum reaches max.
Am I doing anything wrong.
Pls Suggest
while (sum <= max) {
sum = sum + BucWidth; // or sum += bucWidth;
System.out.println("SUmmmmm" + sum);
}
You should check if it is less than or equal to, not if it is not equal to in your while condition, since you want to exit the loop when it reaches the limit.
In general, comparing floating-point numbers for exact equality is asking for trouble unless you have a deep understanding of exactly where and when roundoff will occur. It's safer to use < rather than !=, since the value may never exactly match the one you're expecting.
(This annoyance is one of many reasons that programming languages have int and float as separate datatypes.)
For floating point numbers or long double types it might happen that doing mathematical operations like adding a value to another value might not be equal to a value you assumed as in integer addition operations.
int a=6;
while(sum!=12)
{
sum+=sum;
}
This iterates once.
Consider this
double a=7.4564;
double b=7.4567;
if(a==b){
System.out.println("Both are equal");
}
else{
System.out.println("Both are unequal");
}
Output:-Both are unequal
This is because some number after the decimal can change and so when operations like != are used the numbers have to be exact in all decimal places as used or else the logic won't work. So it is better in your case to use <= instead of != while comparing sum and max.

Java: understanding double (and normalization)

I'm trying to learn a bit on robotics and Java. In my attempt I have written a function that (should) take a uniformed array called q(0.2,0.2,0.2,0.2,0.2), multiply each value in this array by 0.6 or 0.2 based on value Z, compared to another array world (0,1,1,0,0) that represents hit or miss of value Z compared to array world and finally return a new uniformed array.
So:
1.) Loop through (0,1,1,0,0)
2.) if i!=Z than i*0.2, if i=Z than i*0.6
3.) sum all new values in q to a double called normalize
4.) normalize each value in q (value / normalize)
Below is the function:
public static final double pHit = 0.6;
public static final double pMiss = 0.2;
public static int[] world = {0,1,1,0,0};
public static List<Double> q = new ArrayList<Double>();
public static List<Double> sense(int Z){
double normalize = 0;
for(int i=0;i < q.size();i++){
if(Z == world[i]){
q.set(i, (q.get(i) * pHit));
}
else{
q.set(i, (q.get(i) * pMiss));
}
}
//Normalize
for(int i=0;i < q.size();i++){
normalize += q.get(i);
}
for(int i=0;i<q.size();i++){
q.set(i, q.get(i)/normalize);
}
return q;
}
If I set world to (0,1,0,0,0) and Z to 1 I get following results:
0.14285714285714288
0.4285714285714285
0.14285714285714288
0.14285714285714288
0.14285714285714288
This normalizes nicely (sum = 1).
But if I set world to (0,1,1,0,0) and Z to 1 I get a strange result:
0.1111111111111111
0.3333333333333332
0.3333333333333332
0.1111111111111111
0.1111111111111111
This "normalizes" to 0.9999999999999998 ?
Many thanks for any input!
Doubles are not perfect representations of the real number line. They have only about 16 digits of precision. In successive computations, errors can build, sometimes catastrophically. In your case, this has not happened, so be happy.
The value of 0.1 is a nice example. In IEEE floating point, it has only an approximate representation. As a binary fraction, it is 0.0[0011] where the part in square braces repeats forever. This is why floating point numbers (including doubles) may not be the best choice for representing prices.
I highly suggest reading this classic summary:
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Floating point numbers are not exactly represented on computers. For that reason you'll get very small fractions off from exact values when multiplying floating values together.
See this answer as it goes into more depth about floating representations and references this Floating-Point Arithmetic article.
Welcome to the world of floating point numbers!
I did not try to understand your code in detail, but the result 0.9999999999999998 is perfectly normal. I recommend you to read a bit about floating point numbers and their precision.

Adding 1/3 in java results in 1.0, while it shouldn't

Note: question still not answered thoroughly! This questions does not deal with the issue of truncation of floating point parts!!!
In Java I have this simple code:
double sum = 0.0;
for(int i = 1; i <= n; i++){
sum += 1.0/n
}
System.out.println("Sum should be: 1");
System.out.println("The result is: " + sum);
Where n can be any integer. For numbers like 7,9, the expected value for sum is to have difference in the last digits of sum, and the result is 0.999999999998 or something but the output when I use 3 is 1.0.
If you add 1/3 3 times, you would expect a number close to 1, but I get exactly 1.0.
Why?
This is because the division is made in integer.
1/n always gives 0 for n > 1.
Therefore, you always end up with sum = 0 + 1/1 + 0 + 0...
Try with 1.0 / n
If you add 1/3 3 times, you would expect a number close to 1, but I
get exactly 1.0.
Actually a normal person uncontaminated by programming experience would expect n * 1 / n to equal 1, but we're not normal here.
I can't reproduce your problem exactly, I get
groovy:000> def foo(n) {
groovy:001> sum = 0.0
groovy:002> for (int i = 0; i < n; i++) {
groovy:003> sum += 1.0 / n
groovy:004> }
groovy:005> sum
groovy:006> }
===> true
groovy:000> foo(3)
===> 0.9999999999
There may be 2 issues here, at least you will want to be aware of them.
One is that doubles are not exact, they cannot represent some values exactly, and you just have to expect stuff to be off by a little bit. Your goal isn't 100% accuracy, it's to keep the error within acceptable bounds. (Peter Lawrey has an interesting article on doubles that you might want to check out.) If that's not ok for you, you'll want to avoid doubles. For a lot of uses BigDecimal is good enough. If you want a library where the division problems in your question give accurate answers you might check out the answers to this question.
The other issue is that System.out.println doesn't tell you the exact value of a double, it fudges a bit. If you add a line like:
System.out.println(new java.math.BigDecimal(sum));
then you will get an accurate view of what the double contains.
I'm not sure whether this will help clarify things, because I'm not sure what you consider to be the problem.
Here is a test program that uses BigDecimal, as previously suggested, to display the values of the intermediate answers. At the final step, adding the third copy of 1.0/3 to the sum of two copies, the exact answer is half way between 1.0 and the next double lower than it. In that situation the round-to-even rounding rule picks 1.0.
Given that, I think it should round to 1.0, contradicting the question title.
Test program:
import java.math.BigDecimal;
public class Test {
public static void main(String[] args) {
final double oneThirdD = 1.0/3;
final BigDecimal oneThirdBD = new BigDecimal(oneThirdD);
final double twoThirdsD = oneThirdD + oneThirdD;
final BigDecimal twoThirdsBD = new BigDecimal(twoThirdsD);
final BigDecimal exact = twoThirdsBD.add(oneThirdBD);
final double nextLowerD = Math.nextAfter(1.0, 0);
final BigDecimal nextLowerBD = new BigDecimal(nextLowerD);
System.out.println("1.0/3: "+oneThirdBD);
System.out.println("1.0/3+1.0/3: "+twoThirdsBD);
System.out.println("Exact sum: "+exact);
System.out.println("Rounding error rounding up to 1.0: "+BigDecimal.ONE.subtract(exact));
System.out.println("Largest double that is less than 1.0: "+nextLowerBD);
System.out.println("Rounding error rounding down to next lower double: "+exact.subtract(nextLowerBD));
}
}
Output:
1.0/3: 0.333333333333333314829616256247390992939472198486328125
1.0/3+1.0/3: 0.66666666666666662965923251249478198587894439697265625
Exact sum: 0.999999999999999944488848768742172978818416595458984375
Rounding error rounding up to 1.0: 5.5511151231257827021181583404541015625E-17
Largest double that is less than 1.0: 0.99999999999999988897769753748434595763683319091796875
Rounding error rounding down to next lower double: 5.5511151231257827021181583404541015625E-17
An int divided by an int will always produce another int. Now int has no place to store the fractional part of the number so it is discarded. Keep in mind that it is discarded not rounded.
Therefore 1 / 3 = 0.3333333, and the fractional part is discarded meaning that it becomes 0.
If you specify the number as a double (by including the decimal point, ex. 1. or 1.0) then the result will be a double (because java automatically converts an int to a double) and the fractional part will be preserved.
In your updated question, you are setting i to 1.0 but i is still an int. So that 1.0 is getting truncated to 1 and for further calculations, it is still an int. You need to change the type of i to double as well otherwise there will be no difference in the code.
Alternatively you can use sum += 1.0/n
This will have the effect of converting n to a double before performing the calculation

Categories

Resources