Подтвердить что ты не робот

Параметр двойной точности IEEE с плавающей запятой в компиляторе C без типа двойной точности

Я работаю с 8-битным чипом AVR. Нет типа данных для 64-битного двойного (двойные только карты для 32-битного поплавка). Тем не менее, я буду получать 64-битные удваивания по сравнению с Serial и должен выводить 64-разрядные удвоения по сравнению с Serial.

Как я могу конвертировать 64-битный двойной в 32-битный поплавок и обратно без кастования? Формат для 32-разрядных и 64-битных будет соответствовать IEEE 754. Конечно, Я предполагаю потерю точности при преобразовании в 32-битный float.

Для преобразования из 64-битного в 32-битный float я пытаюсь это сделать:

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

Для чисел, таких как 1.0 и 2.0, это работает, но когда я тестировал с передачей в 1.1 в виде 64-битного двоичного кода, выход был отключен бит (буквально, а не каламбуром!), хотя это может быть проблема с моим тестированием. См:

// 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
4b9b3361

Ответ 1

IEEE указывает пять разных режимов округления, но тот, который будет использоваться по умолчанию, Раунд половины даже до. Итак, у вас есть мантисса формы 10001100 11001100 11001100 11001100... и вам нужно округлить ее до 24 бит. Нумерация битов с 0 (наиболее значительная), бит 24 равен 1; но этого недостаточно, чтобы рассказать вам, нужно ли округлить бит 23 или нет. Если все остальные бит равны 0, вы не будете округлять, потому что бит 23 равен 0 (четный). Но оставшиеся биты не равны нулю, поэтому во всех случаях вы округлите.

Некоторые примеры:

10001100 11001100 11001100 10000000... (все ноль) не округляется, потому что бит 23 уже четный.

10001100 11001100 11001101 10000000... (все ноль) округляется, потому что бит 23 является нечетным.

10001100 11001100 1100110x 10000000... 0001 всегда округляется, потому что остальные биты не все равны нулю.

10001100 11001100 1100110x 0xxxxxxx... никогда не округляется, потому что бит 24 равен нулю.

Ответ 2

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

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

Я оставил несколько строк printf, но закомментировал их в комментариях стиля С++. Вам нужно будет предоставить соответствующие определения для reverse_bytes, LITTLE_ENDIAN и dump_bits. Я не хотел портить вам всю радость. Записи в Википедии одинарная точность и цифры двойной точности очень хорошо.

Если вы собираетесь много работать с числами с плавающей запятой, вы должны прочитать "Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой" Дэвид Голдберг и   "Как печатать цифры с плавающей точкой в ​​точности" от Steele и White. Они являются двумя наиболее информативными статьями, когда дело доходит до понимания того, как работают числа с плавающей точкой.

Ответ 3

http://www.google.com/search?q=c+convert+ieee+754+double+single

один из первых результатов:

http://www.mathworks.com/matlabcentral/fileexchange/23173

В коде показано, как преобразовать IEEE-754 в формат IEEE-754 (1,5,10). Этот код содержит много комментариев и упоминает типичные ловушки, в которые вы могли бы попасть.

Это не совсем то, что вы хотите, но это хорошая отправная точка.

Ответ 4

Существует только одна полная реализация IEEE754 double в GCC для AVR, о которой я знаю, и вы можете найти ее здесь.

Вам понадобится этот архив, а затем замените avr_f64.c из архива с этот один.

Библиотека занимает около 21K Flash и 310 байт памяти.

Оригинальное сообщение можно найти здесь. Я извлек всю важную информацию из оригинального сообщения и представлен здесь, так как считаю, что вам нужно иметь учетную запись для входа в форум.