Плавающее в двойное преобразование для Android - PullRequest
2 голосов
/ 30 апреля 2011

ОК, так что все должно быть просто ... Но я все еще не могу понять это.Я пишу программу на Java для Android, чтобы взять FFT массива с плавающей точкой.На возвращенном спектре сложной частоты я извлекаю действительные и мнимые компоненты, чтобы можно было рассчитать некоторые параметры, такие как величина и фаза.Проблема в том, что я использую преобразование libgdx fft, использует float, однако большинство операций класса Math используют double.Это означает, что мне нужно конвертировать число с плавающей точкой в ​​удвоение.Кажется, что он отлично работает на вещественном компоненте БПФ, однако с мнимым я получаю ошибки точности, или, скорее, я получаю для одного частотного бина я получаю мнимое значение с плавающей запятой 45.188522, однако, когда я преобразовываю в удвоенное значение, оно изменяется на -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]));
   }

Я знаю и пробовал класс FloatMath для Android, однако atan2 не реализован, поэтому я вынужден независимо конвертировать в double.

Я также пробовал несколько разных преобразований, таких как:

 imag[i] = tmpi[i];
 imag[i] = Double.parseDouble(Float.toString(tmpi[i])); // Of course you loose accuracy

Но все они по-прежнему возвращают -45.18852 вместо 45.18852

^^^^^ ORIGINAL ^^^^^^

Подробнее:

Ниже приведен мой src-код и использование для заинтересованных.

Хорошо, я использую Ubuntu 10.10 с Eclipse JDK, Версия: Helios ServiceВыпуск 2 Android SDK: последняя версия r10 от разработчиков Android .Я компилирую для Android 1.6, API уровня 4. Я использую libgdx для fft, вы можете получить его здесь, Libgdx и убедиться, что вы добавили gdx.jar в свои библиотеки и добавили в свои библиотеки путей сборки.Если вы создаете новый проект с той же или новой активностью для Android 1.6, настраиваете AVD, у того, что я настроил, есть следующая поддержка (включенная для полноты):

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

Вот мойкод src:

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);

    }
  }

Я новичок в разработке для Android и Java, поэтому, пожалуйста, будьте терпеливы со мной, если ответ кажется очевидным или мой синтаксис кажется странным.Надеюсь, кто-нибудь решит это ...

Ответы [ 3 ]

2 голосов
/ 30 апреля 2011

Я думаю, вы найдете проблему в этой строке:

float[] fft_cpx,tmpr,tmpi,mod_spec = new float[array.length/2];

Я думаю, что вы создаете экземпляр только одного массива, если вы замените его на:

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];

Таким образом, создав несколько отдельных объектов, вы обнаружите, что tmpi и img совпадают.

2 голосов
/ 30 апреля 2011

Возможно, стоит попробовать Double.parseDouble(new String(tmpr[i])) вместо неявного приведения: это НЕ ДОЛЖНО иметь значение, но я видел такие странные вещи с плавающей точкой.

1 голос
/ 30 апреля 2011

@ digiphd,

Это не ответ ... Это моя скромная попытка доказать, что в средах Java нет ничего плохого в расширении преобразования с плавающего на двойное илисужающее преобразование от двойного к плавающему.

Не могли бы вы запустить этот тест (или эквивалентный) в вашей среде?

Кроме этого, единственное, что я могу предложить, это ДВОЙНОЙ ПРОВЕРЬТЕ объявления переменныхи следуйте стандарту объявления каждой переменной в отдельной строке ... и инициализируйте каждую переменную, как вы объявите, что побочным эффектом перемещения каждой переменной "вниз" в ее наименьшую возможную область видимости;Как правило, это считается «хорошей вещью», потому что сводит к минимуму возможность случайного использования / изменения переменной в другом месте.Это идет в три раза для «временных» переменных !!!

«Доказательство», которые плавают? ВСЕГДА?расширяется до эквивалентного двойного

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);
  }

}

ВЫХОД

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: Нет, как и ожидалось.

...