Iniciar sesión

Ver la versión completa : Duda con un programa de C que me está volviendo loco



A600
17/08/2012, 03:32
¿Por qué en este código senderID1 y senderID2 no valen lo mismo?



var1 = channel.Service_ID;
var2 = channel.Audio_PID*powf(2, 16);
var3 = (channel.TunerType + 1)*powf(2, 29);
var4 = channel.TransportStream_ID*powf(2, 32);
var5 = ((channel.TunerType-1)*500*10)*powf(2, 48);
var6 = (channel.Flags & (1 << 3)?1:2)*powf(2, 61);

senderID1 = var1 + var2 + var3 + var4 + var5 + var6;
senderID2 = channel.Service_ID + channel.Audio_PID*powf(2, 16) + (channel.TunerType + 1)*powf(2, 29) + channel.TransportStream_ID*powf(2, 32) + ((channel.TunerType-1)*500*10)*powf(2, 48) + (channel.Flags & (1 << 3)?1:2)*powf(2, 61);

El resultado es éste:


senderID1 = 2305847528600401057
senderID2 = 2305847528600400896

y el resultado correcto es el de senderID1. ¿Será cosa del Visual Studio?

chipan
17/08/2012, 03:37
¿Sera cosa de que las variables se actualizan entre el calculo de senderID1 y senderID2?

Tambien se me ocurre que alguno de los valores que se asignan a las variables sobrepasen el valor de las mismas y se pierda información, o que las variables sean enteras y el valor que se calcula para las mismas tenga cifras decimales que se desechan y que por eso el calculo directo difiera del precalculado.

A600
17/08/2012, 03:45
No. Era un problema de casting que con tanto long long se me va la pinza:


senderID2 = long long(channel.Service_ID) + long long(channel.Audio_PID*powf(2, 16)) + long long((channel.TunerType + 1)*powf(2, 29)) + long long(channel.TransportStream_ID*powf(2, 32)) + long long(((channel.TunerType-1)*500*10)*powf(2, 48)) + long long((channel.Flags & (1 << 3)?1:2)*powf(2, 61));


senderID1 = 2305847528600401057
senderID2 = 2305847528600401057

chipan, no vi antes tu edición pero el problema es que channel.Service_ID es un entero y el resto de las operaciones son long long, así que la solución menos guarra es:


senderID2 = channel.Service_ID + long long(channel.Audio_PID*powf(2, 16) + (channel.TunerType + 1)*powf(2, 29) + channel.TransportStream_ID*powf(2, 32) + ((channel.TunerType-1)*500*10)*powf(2, 48) + (channel.Flags & (1 << 3)?1:2)*powf(2, 61));

chipan
17/08/2012, 04:13
No. Era un problema de casting que con tanto long long se me va la pinza:


senderID2 = long long(channel.Service_ID) + long long(channel.Audio_PID*powf(2, 16)) + long long((channel.TunerType + 1)*powf(2, 29)) + long long(channel.TransportStream_ID*powf(2, 32)) + long long(((channel.TunerType-1)*500*10)*powf(2, 48)) + long long((channel.Flags & (1 << 3)?1:2)*powf(2, 61));


senderID1 = 2305847528600401057
senderID2 = 2305847528600401057

chipan, no vi antes tu edición pero el problema es que channel.Service_ID es un entero y el resto de las operaciones son long long, así que la solución menos guarra es:


senderID2 = channel.Service_ID + long long(channel.Audio_PID*powf(2, 16) + (channel.TunerType + 1)*powf(2, 29) + channel.TransportStream_ID*powf(2, 32) + ((channel.TunerType-1)*500*10)*powf(2, 48) + (channel.Flags & (1 << 3)?1:2)*powf(2, 61));

Bueno, entonces no iba desencaminado XD

hardyx
17/08/2012, 09:21
Es que usas potencias muy grandes, es normal que tengas que usar long long. Por cierto, en C el casting se hace poniendo el tipo entre paréntesis así, no sea que te coja otra cosa: (long long)(expresión). También podrias usar el tipo int64_t, que es estándar. Otra cosa importante, haz el casting a los operadores directamente, ya que si haces (long long) (a * b), no es lo mismo que (long long) a * b, ya que en el primer caso (a*b) se te puede desbordar.

También si los números que estás operando son enteros, puedes hacer N<<M para multiplicar por potencias de 2. Por ejemplo: N<<3 es lo mismo que N * powf(2, 3). Queda más limpio cuando se manejan bits, además de que al pasarlo a double puedes perder precisión. Ojo, esto sólo si son enteros los operadores.

A600
17/08/2012, 12:24
Es que usas potencias muy grandes, es normal que tengas que usar long long. Por cierto, en C el casting se hace poniendo el tipo entre paréntesis así, no sea que te coja otra cosa: (long long)(expresión). También podrias usar el tipo int64_t, que es estándar. Otra cosa importante, haz el casting a los operadores directamente, ya que si haces (long long) (a * b), no es lo mismo que (long long) a * b, ya que en el primer caso (a*b) se te puede desbordar.

También si los números que estás operando son enteros, puedes hacer N<<M para multiplicar por potencias de 2. Por ejemplo: N<<3 es lo mismo que N * powf(2, 3). Queda más limpio cuando se manejan bits, además de que al pasarlo a double puedes perder precisión. Ojo, esto sólo si son enteros los operadores.

¡Gracias por los consejos! Lo de N<<M lo pensé luego así que voy a ver si lo dejo más apañado :)

edito: Aquí la solución sin errores:


senderID2 = channel.Service_ID + ((uint64_t)channel.Audio_PID<<16) + ((uint64_t)(channel.TunerType + 1)<<29) + ((uint64_t)channel.TransportStream_ID<<32) + ((uint64_t)((channel.TunerType-1)*500*10)<<48) + ((uint64_t)(channel.Flags & (1 << 3)?1:2)<<61);

Segata Sanshiro
17/08/2012, 18:59
¿Y de qué decodificador de TDT estás haciendo el firmware? :D

A600
17/08/2012, 20:47
¿Y de qué decodificador de TDT estás haciendo el firmware? :D

Es un PVR para usar el DVBViewer con el XBMC:

http://img.photobucket.com/albums/v295/_A600/xbmc/PVR1.png

A600
19/08/2012, 02:13
Tengo otro problemilla:


double delphiTimeStart = 1345341600.0/86400.0;

devuelve el valor correcto: 15571.083333333334

pero


double var1 = 1345341600.0;
double delphiTimeStart = var1/86400.0;

me devuelve 15571.083007812500

¿Alguien sabe qué pasa aquí?



edito:

Si declaro las variables como globales sí obtengo el resultado correcto, pero como locales no :(

El caso es que este simple programa con variables locales:


#include <stdio.h>

void main()
{
double var1 = 1345341600.0;
double var2 = var1/86400.0;
printf("Resultado = %f\n", var2);
}

sí funciona, así que npi de por qué la DLL que carga el XBMC no. Estas son las cosas inexplicables que le comen a uno la moral :(

otto_xd
19/08/2012, 11:42
Una pregunta, ya que hace un tiempo que no toco C.

Entiendo que pow y powf sirven para elevar x a y, pero su funcion es crear ids con mascaras de bits o para que lo haces asi?

Me ha picado la curiosidad xD

A600
19/08/2012, 13:23
Entiendo que pow y powf sirven para elevar x a y, pero su funcion es crear ids con mascaras de bits o para que lo haces asi?

Para eso mismo. Estos son los datos que saco:


Bit 0...15 ServiceID
Bit 16...28 AudioPID
Bit 29...31 TunerType + 1
// neu //
Bit 32...47 TransportstreamID
Bit 48...60 OrbitalPos
Bit 61...62 TV/Radio Flag (0 = undefined, 1 = TV, 2 = Radio)

otto_xd
19/08/2012, 13:59
Muchas gracias, nunca se me habria ocurrido crear IDS con mascaras usando esas funciones, es mas, he mirado ejercicios que tengo de mascaras y lo hago de una forma mucho mas burda.

Pasando a lo que estas haciendo, tiene soporte para multiples sintonizadores?Es un trabajo muy grande ese que estas haciendo.

Siempre pense que xmbc usaba lenguajes de script para toda esa logica, y me entero que va en C :D

A600
19/08/2012, 17:30
Pasando a lo que estas haciendo, tiene soporte para multiples sintonizadores?

Sí. Yo tengo una tarjeta de SAT y un pincho TDT y acabo de pedir un motor para la paellera para sacarle todo el jugo.


Es un trabajo muy grande ese que estas haciendo.

Ni de coña. Estoy usando un PVR ya existente (el Vu+) que voy modificando con ñapas para que funcione con el DVBViewer :D

A600
19/08/2012, 21:21
Uso Visual Studio con las optimizaciones desactivadas. Como digo más arriba, este código funciona:


#include <stdio.h>

void main()
{
double var1 = 1345341600.0;
double var2 = var1/86400.0;
printf("Resultado = %f\n", var2);
}

Pero el mismo código, incrustado en una función del PVR no.

Necesito esa función para convertir de un time_t a un floatdatetime que es lo que necesita la API del DVBViewer: Delphitime = (unixtime/86400)+25569, siendo la parte entera los días y la parte decimal los segundos de un día, pero con los resultados que obtengo me salen variaciones de minutos inaceptables.

Al final lo que he hecho ha sido convertir el time_t con gmtime y sacar los datos de ahí, pero me parece un churro de solución.

swapd0
19/08/2012, 21:42
Con el llvm 3.0 y el gcc 4.2 dan el mismo resultado.

Ten en cuenta que en los x86 (no se desde que version) independientemente de que uses floats o doubles, internamente usa numeros de 80bits y al final hace el redondeo a double o float.

Si la operacion fuese mas larga tendria sentido que te pudieran dar valores distintos por redondeos internos, pero en este caso la verdad es que no lo entiendo.

A600
19/08/2012, 22:09
¡Solucionado!

Mirando las opciones de compilación de Visual Studio he visto que las optimizaciones para SSE2 no estaban activadas en el proyecto de la DLL. Ahora sí que me da los valores correctos, así que gracias a id6490 por ponerme sobre la pista :)

xtuca
19/08/2012, 22:35
*****,esto es chino para el 99% de la poblacion.