2010-08-12 11 views
3

Je dois convertir de façon répétée 1024+ flottants consécutifs de 4 octets (plage -1 à 1) en courts-circuits de 2 octets (plage -32768 à 32767) et écrire sur le disque.Façon rapide de convertir le flotteur de la gamme -1 à 1 à court?

Actuellement, je le fais avec une boucle:

short v = 0; 
for (unsigned int sample = 0; sample < length; sample++) 
{ 
    v = (short)(inbuffer[sample * 2] * 32767.0f); 
    fwrite(&v, 2, 1, file); 
} 

Et cela fonctionne, mais le point flottant calc et la boucle est cher. Y a-t-il un moyen de l'optimiser?

+2

Attention: si -1.0 se traduit par -32768 et 0.0 se traduit par 0, alors +1.0 devrait se traduire par +32768, ce qui n'est pas dans la plage 'short'. – mouviciel

+0

Peut-être que l'OP signifie "de -1 jusqu'à 1". Même ainsi, les erreurs d'arrondi à virgule flottante peuvent causer des problèmes. Il serait peut-être préférable d'enregistrer temporairement le calcul dans un int, puis de vérifier sa valeur avant de l'enregistrer dans un float. – David

+0

Bon point, -32767 à 32767 devrait aller. – Morrowless

Répondre

6
short v = 0; 
for (unsigned int sample = 0; sample < length; sample++) 
{ 
    v = (short)(inbuffer[sample * 2] * 32767.0f); 
    // The problem is not here-------^^^^^^^^^^^ 
    fwrite(&v, 2, 1, file);   
    // it is here ^^^^^^^ 
} 

Un Mac typique (? Tag objectif-c, ou parlons-nous ici iphone) peut faire milliards de multiplications de flotteur par seconde. fwrite est cependant un appel de bibliothèque, qui suit certaines indirections pour écrire ses données dans un tampon et éventuellement le vider. Il est préférable de remplir votre propre tampon dans un lot:

short v[SZ] = 0; 
// make sure SZ is always > length, or allocate a working buffer on the heap. 
for (unsigned int sample = 0; sample < length; sample++) 
{ 
    v[sample] = (short)(inbuffer[sample * 2] * 32767.0f); 
} 
fwrite(v,sizeof(v),1,file); 
2

J'aurais pensé que les appels répétés à fwrite seraient la partie la plus coûteuse. Que diriez-vous:

short outbuffer[length]; // note: you'll have to malloc this if length isn't constant and you're not using a version of C that supports dynamic arrays. 
for (unsigned int sample = 0; sample < length; sample++) 
{ 
    outbuffer[sample] = (short)(inbuffer[sample * 2] * 32767.0f); 
} 
fwrite(outbuffer, sizeof *outbuffer, length, file); 
+0

Même s'il a des tableaux dynamiques, ce ne serait pas une bonne idée de les utiliser dans un contexte où vous ne connaissez pas une limite sur la taille. Méfiez-vous du débordement de pile. –

2

je suppose, que le goulot d'étranglement de la boucle peut être pas court à flotter la conversion, mais la sortie écriture dans le fichier - essayez de déplacer la sortie de fichier en dehors de la boucle

short v = 0; 
short outbuffer = // create outbuffer of required size 
for (unsigned int sample = 0; sample < length; sample++) 
{ 
    outbuffer[sample] = (short)(inbuffer[sample * 2] * 32767.0f); 
} 

fwrite(outbuffer, 2, sizeof(outbuffer), file); 
0

Vous pouvez essayer quelque chose comme ceci:

out[i] = table[((uint32_t *)in)[i]>>16]; 

table est une table de consultation qui mappe les 16 bits supérieurs d'un flotteur IEEE au int16_t valeur que vous voulez. Cependant, cela va perdre de la précision. Vous devez conserver et utiliser 23 bits (1 bit de signe, 8 bits d'exposant et 14 bits de mantisse) pour une précision totale, ce qui signifie une table de 16 Mo, ce qui va tuer la cohérence du cache et donc la performance. Etes-vous sûr que les conversions en virgule flottante sont lentes?

Etes-vous sûr que les conversions en virgule flottante sont lentes? Tant que vous utilisez fwrite de cette façon, vous dépensez 50 à 100 fois plus de temps CPU en fwrite qu'en arithmétique en virgule flottante. Si vous traitez ce problème et que le code est encore trop lent, vous pouvez utiliser une approche consistant à ajouter un biais magique et à lire les bits de la mantisse pour les convertir en int16_t au lieu de les multiplier par 32767.0. Cela pourrait ou pourrait ne pas être plus rapide.