simple punto precisión flotante float ejemplos doble c++ c casting avr
this

c++ - precisión - punto flotante doble precision



Analice el punto flotante IEEE de precisión doble en un compilador de C sin el tipo de precisión doble (4)

El siguiente código parece convertir de precisión simple a precisión doble. Lo dejo como un ejercicio para que el lector implemente la versión de reducción. Esto debería hacer que empieces. La parte más difícil es obtener las posiciones de bit correctas en el significado. Incluyo algunos comentarios que incluyen lo que está pasando.

double extend_float(float f) { unsigned char flt_bits[sizeof(float)]; unsigned char dbl_bits[sizeof(double)] = {0}; unsigned char sign_bit; unsigned char exponent; unsigned int significand; double out; memcpy(&flt_bits[0], &f, sizeof(flt_bits)); /// printf("---------------------------------------/n"); /// printf("float = %f/n", f); #if LITTLE_ENDIAN reverse_bytes(flt_bits, sizeof(flt_bits)); #endif /// dump_bits(&flt_bits[0], sizeof(flt_bits)); /* IEEE 754 single precision * 1 sign bit flt_bits[0] & 0x80 * 8 exponent bits flt_bits[0] & 0x7F | flt_bits[1] & 0x80 * 23 fractional bits flt_bits[1] & 0x7F | flt_bits[2] & 0xFF | * flt_bits[3] & 0xFF * * E = 0 & F = 0 -> +/- zero * E = 0 & F != 0 -> sub-normal * E = 127 & F = 0 -> +/- INF * E = 127 & F != 0 -> NaN */ sign_bit = (flt_bits[0] & 0x80) >> 7; exponent = ((flt_bits[0] & 0x7F) << 1) | ((flt_bits[1] & 0x80) >> 7); significand = (((flt_bits[1] & 0x7F) << 16) | (flt_bits[2] << 8) | (flt_bits[3])); /* IEEE 754 double precision * 1 sign bit dbl_bits[0] & 0x80 * 11 exponent bits dbl_bits[0] & 0x7F | dbl_bits[1] & 0xF0 * 52 fractional bits dbl_bits[1] & 0x0F | dbl_bits[2] & 0xFF * dbl_bits[3] & 0xFF | dbl_bits[4] & 0xFF * dbl_bits[5] & 0xFF | dbl_bits[6] & 0xFF * dbl_bits[7] & 0xFF * * E = 0 & F = 0 -> +/- zero * E = 0 & F != 0 -> sub-normal * E = x7FF & F = 0 -> +/- INF * E = x7FF & F != 0 -> NaN */ dbl_bits[0] = flt_bits[0] & 0x80; /* pass the sign bit along */ if (exponent == 0) { if (significand == 0) { /* +/- zero */ /* nothing left to do for the outgoing double */ } else { /* sub-normal number */ /* not sure ... pass on the significand?? */ } } else if (exponent == 0xFF) { /* +/-INF and NaN */ dbl_bits[0] |= 0x7F; dbl_bits[1] = 0xF0; /* pass on the significand */ } else { /* normal number */ signed int int_exp = exponent; int_exp -= 127; /* IEEE754 single precision exponent bias */ int_exp += 1023; /* IEEE754 double precision exponent bias */ dbl_bits[0] |= (int_exp & 0x7F0) >> 4; /* 7 bits */ dbl_bits[1] = (int_exp & 0x00F) << 4; /* 4 bits */ } if (significand != 0) { /* pass on the significand most-significant-bit first */ dbl_bits[1] |= (flt_bits[1] & 0x78) >> 3; /* 4 bits */ dbl_bits[2] = (((flt_bits[1] & 0x07) << 5) | /* 3 bits */ ((flt_bits[2] & 0xF8) >> 3)); /* 5 bits */ dbl_bits[3] = (((flt_bits[2] & 0x07) << 5) | /* 3 bits */ ((flt_bits[3] & 0xF8) >> 3)); /* 5 bits */ dbl_bits[4] = ((flt_bits[3] & 0x07) << 5); /* 3 bits */ } ///dump_bits(&dbl_bits[0], sizeof(dbl_bits)); #if LITTLE_ENDIAN reverse_bytes(&dbl_bits[0], sizeof(dbl_bits)); #endif memcpy(&out, &dbl_bits[0], sizeof(out)); return out; }

Dejé algunas líneas de printf pero comenté en comentarios de estilo C ++. Deberá proporcionar las definiciones adecuadas para reverse_bytes , LITTLE_ENDIAN y dump_bits . No quería arruinar toda la diversión para ti después de todo. Las entradas de Wikipedia sobre precisión simple y números de precisión doble son muy buenas.

Si va a jugar mucho con los números de punto flotante, debe leer "Lo que todo científico informático debe saber sobre la aritmética de punto flotante" de David Goldberg y "Cómo imprimir los números de punto flotante con precisión" por Steele y White. Son los dos artículos más informativos que existen sobre la comprensión de cómo funcionan los números de punto flotante.

Estoy trabajando con un chip AVR de 8 bits. No hay ningún tipo de datos para un doble de 64 bits (el doble solo se asigna al flotador de 32 bits). Sin embargo, recibiré dobles de 64 bits sobre Serial y tendré que generar dobles de 64 bits sobre Serial.

¿Cómo puedo convertir el doble de 64 bits en un flotador de 32 bits y viceversa sin lanzar? El formato tanto para el de 32 bits como para el de 64 bits seguirá el IEEE 754. Por supuesto, asumo una pérdida de precisión al convertir al flotador de 32 bits.

Para la conversión de flotación de 64 bits a 32 bits, estoy probando esto:

// Script originally from http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1281990303 float convert(uint8_t *in) { union { float real; uint8_t base[4]; } u; uint16_t expd = ((in[7] & 127) << 4) + ((in[6] & 240) >> 4); uint16_t expf = expd ? (expd - 1024) + 128 : 0; u.base[3] = (in[7] & 128) + (expf >> 1); u.base[2] = ((expf & 1) << 7) + ((in[6] & 15) << 3) + ((in[5] & 0xe0) >> 5); u.base[1] = ((in[5] & 0x1f) << 3) + ((in[4] & 0xe0) >> 5); u.base[0] = ((in[4] & 0x1f) << 3) + ((in[3] & 0xe0) >> 5); return u.real; }

Para números como el 1.0 y el 2.0, lo anterior funciona, pero cuando probé pasar un 1.1 como un doble de 64 bits, la salida se apagó un poco (¡literalmente, no un juego de palabras!), Aunque esto podría ser un problema con mis pruebas Ver:

// Comparison of bits for a float in Java and the bits for a float in C after // converted from a 64-bit double. Last bit is different. // Java code can be found at https://gist.github.com/912636 JAVA FLOAT: 00111111 10001100 11001100 11001101 C CONVERTED FLOAT: 00111111 10001100 11001100 11001100


IEEE especifica cinco modos de redondeo diferentes , pero el que se usará de forma predeterminada es la mitad redonda o par . Así que tienes una mantisa de la forma 10001100 11001100 11001100 11001100 ... y tienes que redondearla a 24 bits. Al numerar los bits desde 0 (el más significativo), el bit 24 es 1; pero eso no es suficiente para decirle si debe redondear el bit 23 o no. Si todos los bits restantes fueran 0, no se redondearía, porque el bit 23 es 0 (par). Pero los bits restantes no son cero, por lo que se redondea en todos los casos.

Algunos ejemplos:

10001100 11001100 11001100 10000000 ... (todo cero) no se redondea hacia arriba, porque el bit 23 ya es par.

10001100 11001100 11001101 10000000 ... (todo cero) se redondea hacia arriba, porque el bit 23 es impar.

10001100 11001100 1100110x 10000000 ... 0001 siempre se redondea, porque los bits restantes no son todos cero.

10001100 11001100 1100110x 0xxxxxxx ... nunca se redondea, porque el bit 24 es cero.


Tengo conocimiento de una sola implementación completa de IEEE754 en GCC para AVR , y puede encontrarla here .

Necesitará this archivo y luego reemplace avr_f64.c del archivo con this .

La biblioteca toma alrededor de 21K Flash y 310 bytes de RAM.

La publicación original se puede encontrar here . He extraído toda la información importante de la publicación original y la he presentado aquí, ya que creo que necesitas tener una cuenta para iniciar sesión en el foro.