Float to Double conversion for android

10,701

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.

Share:
10,701
digiphd
Author by

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, 2022

Comments

  • digiphd
    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
    digiphd almost 13 years
    Thanks, I just edited it for that reason.. I assume you mean: Double.parseDouble(Float.toString(tmpi[i]));
  • Stephen C
    Stephen C almost 13 years
    that kind of thing should not be necessary. For a start, it is a very expensive way to convert from float to double. Please provide a concrete example (with actual code if possible) where this was supposedly necessary.
  • digiphd
    digiphd almost 13 years
    Yeah 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
    digiphd almost 13 years
    This wasn't the solution, I have provided example code above :)
  • Stephan
    Stephan almost 13 years
    The desktop Java VM has (more or less) nothing todo with dalvik running the code in the problem.
  • corlettk
    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
    Stephan almost 13 years
    The 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
    Stephan almost 13 years
    So, 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.