Float to Double conversion for android
Solution 1
Might want to try Double.parseDouble(new String(tmpr[i]))
instead of the implicit cast: it SHOULDN'T make a difference, but I've seen strange things like that with floats.
Solution 2
I think you'll find the problem is this line:
float[] fft_cpx,tmpr,tmpi,mod_spec = new float[array.length/2];
I think you are only instantiating one array, if you replace it with:
float[] fft_cpx = new float[array.length/2];
float[] tmpr = new float[array.length/2];
float[] tmpi = new float[array.length/2];
float[] mod_spec = new float[array.length/2];
Thereby creating some separate objects, you will find that tmpi and img match.
Solution 3
@digiphd,
This isn't an answer... it's my humble attempt at a proof that there's nothing wrong with the Java environments widenning conversion from float to double, or the narrowing conversion from double to float.
Would you please run this test (or equivalent) in your environment?
Other than that, the only thing I can suggest is DOUBLE CHECK your variable declarations, and follow the standard of declaring each variable on its own line... and initialise-as-you-declare each variable, which has the side-effect of moving each variable "down" into it's smallest-possible-scope; This is generally regarded as a "good thing", because it minimises opportunity for a variable to be accidentally used/modified somewhere-else unintentionally. This goes TRIPLE for "temporary" variables!!!
"PROOF" that float ?ALLWAYS? widens to an EQUIVALENT double
package forums;
import java.util.Random;
public class FunkyDouble
{
private static final Random RANDOM = new Random();
private static final int HOW_MANY = 100*1000*1000;
private final static double EPSILON = 0.0000001; // 1/10^7
public static void main(String... args)
{
double d;
float f;
for (int i=0; i<HOW_MANY; i++) {
d = randomDouble();
f = (float) d;
if ( Math.abs(d - f) > EPSILON ) {
System.out.println("FunkyDouble A: Math.abs("+d+" - "+f+") = "+Math.abs(d - f));
}
f = randomFloat();
d = f;
if ( Math.abs(f - d) > EPSILON ) {
System.out.println("FunkyDouble B: Math.abs("+f+" - "+d+") = "+Math.abs(f - d));
}
}
}
private static double randomDouble() {
return RANDOM.nextDouble() * (RANDOM.nextBoolean() ? -1.0D : 1.0D);
}
private static float randomFloat() {
return RANDOM.nextFloat() * (RANDOM.nextBoolean() ? -1.0F : 1.0F);
}
}
OUTPUT
C:\Java\home\src\forums>"C:\Program Files\Java\jdk1.6.0_16\bin\java.exe" -Xms4m -Xmx256m -enableassertions -cp c:\java\home\src;C:\Java\home\classes; forums.FunkyDouble
Press any key to continue . . .
IE: None, as expected.
digiphd
Linux enthusiast, Microelectronic engineer, Artifical Intelligence, Machine Learning. Operations manager and lead engineer at Huntedhive.com - Python web application development in Australia.
Updated on June 04, 2022Comments
-
digiphd almost 2 years
OK, so this should be simple... But I still can't figure it out. I am writing a program in java for Android to take the fft of a float array. On the complex frequency spectrum returned I extract the real and imaginary components so I can calculate some parameters such as magnitude and phase. The problem is the libgdx fft transform I am using uses float, however most of the Math class operations use double. So that means I need to convert float to double. It seems to work fine on the Real component of the fft, however with the imaginary I get precision errors, or rather I get for one frequency bin I get an imaginary float value of 45.188522 however, when I convert to double it changes to -45.188522.
fft.forward(array); fft_cpx=fft.getSpectrum(); tmpi = fft.getImaginaryPart(); tmpr = fft.getRealPart(); for(int i=0;i<array.length/2;i++) { real[i] = (double) tmpr[i]; imag[i] = (double) tmpi[i]; // THIS CONVERSION mag[i] = (float)Math.sqrt((real[i]*real[i]) + (imag[i]*imag[i])); phase[i] = (float) ((float) Math.atan2(imag[i], real[i])); }
I am aware and have tried the android FloatMath class, however there is no atan2 implemented so I am forced to convert to double regardless.
I have also tried a few different conversions like:
imag[i] = tmpi[i]; imag[i] = Double.parseDouble(Float.toString(tmpi[i])); // Of course you loose accuracy
But all still return a -45.18852 instead of 45.18852
^^^^^ ORIGINAL ^^^^^^
More Detail:
Below is my src code and usage for those interested.
Ok, I am using Ubuntu 10.10 with eclipse JDK, Version: Helios Service Release 2 Android SDK: The latest r10 from android developers. I am compiling for android 1.6, API level 4. I am using libgdx for the fft you can get it here, Libgdx and ensure you add the gdx.jar to your libs and added to your build path libraries. If you create a new project either with the same or new activity for android 1.6, set up a AVD, the one I have set up has the following support (included for completeness) :
SD Card yes Accellerometer yes DPas Support yes Abstracted LCD Density 240 Audio Playback Support yes Max VM Application heap size 24 camera support no Touch Screen support yes
Here is my src code:
package com.spec.example; import android.app.Activity; import android.os.Bundle; import com.badlogic.gdx.audio.analysis.FFT; import java.lang.String; import android.util.FloatMath; import android.widget.TextView; public class spectrogram extends Activity { /** Called when the activity is first created. */ float[] array = {1, 6, 1, 4, 5, 0, 8, 7, 8, 6, 1,0, 5 ,6, 1,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; float[] array_hat,res=new float[array.length/2]; float[] fft_cpx,tmpr,tmpi,mod_spec =new float[array.length/2]; float[] real_mod = new float[array.length], imag_mod = new float[array.length]; double[] real = new double[array.length], imag= new double[array.length]; double[] mag = new double[array.length] ,phase = new double[array.length]; int n; float tmp_val; String strings; FFT fft = new FFT(32, 8000); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tv = new TextView(this); fft.forward(array); fft_cpx=fft.getSpectrum(); tmpi = fft.getImaginaryPart(); tmpr = fft.getRealPart(); for(int i=0;i<array.length;i++) { real[i] = (double) tmpr[i]; // This works well imag[i] = (double) tmpi[i]; // However this is creates a problem //mag[i] = FloatMath.sqrt((tmpr[i]*tmpr[i]) + (tmpi[i]*tmpi[i])); //using FloatMath android class (works fine) mag[i] = Math.sqrt((real[i]*real[i]) + (imag[i]*imag[i])); phase[i]=Math.atan2(imag[i],real[i]); /****Reconstruction****/ real_mod[i] = (float) (mag[i] * Math.cos(phase[i])); imag_mod[i] = (float) (mag[i] * Math.sin(phase[i])); } fft.inverse(real_mod,tmpi,res);// inverse fft to reconstruct original array if input = output It works as it is, however it is using the input imaginary, not imag_mod strings=String.valueOf(tmpi[1]); // Just printing the second imaginary element Calculated using: |X|e^(j*phaseofX) = |X|(cos(X) + jsin(X)) //strings=String.valueOf(imag_mod[1]); // Just printing the second imaginary element (Original returned from fft.getImaginary()) //this ^^ is the one which returns a -ve (Uncomment to test) tv.setText(strings); setContentView(tv); } }
I am new to android development and java, so please be patient with me if the answer seems obvious or my syntax seems odd. Hopefully someone work it out...
-
digiphd almost 13 yearsThanks, I just edited it for that reason.. I assume you mean: Double.parseDouble(Float.toString(tmpi[i]));
-
Stephen C almost 13 yearsthat kind of thing should not be necessary. For a start, it is a very expensive way to convert from
float
todouble
. Please provide a concrete example (with actual code if possible) where this was supposedly necessary. -
digiphd almost 13 yearsYeah I am in the process of providing more code, I am not using the method stated above. But either way it still returns a negative. Editing original post now to add usable code. Thanks
-
digiphd almost 13 yearsThis wasn't the solution, I have provided example code above :)
-
Stephan almost 13 yearsThe desktop Java VM has (more or less) nothing todo with dalvik running the code in the problem.
-
corlettk almost 13 years@Stephan: Huh? Would you care to elaborate on that, please? As I understand things, the problem is:
doubles[i] = (double) floats[i];
produces a double-value such that (float)doubles[i] approximately equals floats[i]***-1**... or do you mean that you think this a problem in the Andriod device (presumably a mobile phone) as apposed to the desktop development environment? In which case you might be onto something. -
Stephan almost 13 yearsThe Java VM on the desktop is something different than the dalvik VM on Android. Although they seem to run the same code, they are different pieces of software (that's what the Google vs Oracle lawsuit is about, I guess). Most obvious, dalvik runs files in the DEX format and not the bytecode compiled from the java compiler (dx is the tool which converts the one into the other). [The wikipedia entry](en.wikipedia.org/wiki/Dalvik_(software) gives a first hint on the differences.
-
Stephan almost 13 yearsSo, if there would be a problem in the dalvik VM regarding casting floats to doubles (which I highly doubt, but anyway), you could not proove the contrary by using the desktop VM. So your test is not wrong per se, but running it on the Java VM does not yield meaningful results, if we were up to chasing a bug in the dalvik VM. My first comment might have been a bit harsh, though.