23

私は8ビットAVRチップを使用しています。64ビットdoubleのデータ型はありません(doubleは32ビットfloatにマップされるだけです)。ただし、シリアルで64ビットのdoubleを受信するため、シリアルで64ビットのdoubleを出力する必要があります。

64ビットのdoubleを32ビットのfloatに変換し、キャストせずに元に戻すにはどうすればよいですか?32ビットと64ビットの両方の形式はIEEE754に準拠します。もちろん、32ビット浮動小数点に変換すると精度が低下すると思います。

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ビットのdoubleとして渡すことでテストした場合、出力は少しずれていました(文字通り、しゃれではありません!)。私のテスト。見る:

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

4 に答える 4

5

IEEEでは5つの異なる丸めモードが指定されていますが、デフォルトで使用されるのは、半分から偶数への丸めです。したがって、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がゼロであるため、切り上げられません。

于 2011-04-10T21:06:32.523 に答える
3

次のコードは、単精度から倍精度に変換されているようです。ナローイングバージョンを実装するための演習として、読者に任せます。これで始められるはずです。最も難しい部分は、仮数のビット位置を正しくすることです。何が起こっているのかを含むコメントをいくつか含めます。

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ましたが、C++スタイルのコメントでコメントアウトしました。reverse_bytes、、、LITTLE_ENDIANおよびの適切な定義を提供する必要がありdump_bitsます。結局のところ、私はあなたのためにすべての楽しみを台無しにしたくありませんでした。単精度および倍精度の数値に関するウィキペディアのエントリは非常に優れています。

浮動小数点数を何度もいじくり回す場合は、DavidGoldbergによる「すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと」とSteeleandWhiteによる 「浮動小数点数を正確に印刷する方法」を読む必要があります。これらは、浮動小数点数がどのように機能するかを理解する上で最も有益な2つの記事です。

于 2011-04-10T22:11:35.533 に答える
1

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

最初の結果の1つはこれです:

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

このコードは、IEEE-754 doubleをIEEE-754のような(1,5,10)浮動小数点形式に変換する方法を示しています。このコードには多くのコメントが含まれており、陥る可能性のある典型的なトラップについて言及しています。

それはあなたが望むものではありませんが、それは良い出発点です。

于 2011-04-10T20:34:13.970 に答える
1

私が知っているAVR用のGCCでのIEEE754doubleの完全な実装は1つだけであり、ここで見つけることができます。

このアーカイブが必要になり、アーカイブのavr_f64.cをこれに置き換えます。

ライブラリには約21Kのフラッシュと310バイトのRAMが必要です。

元の投稿はここにあります。フォーラムにログインするにはアカウントが必要だと思うので、元の投稿からすべての重要な情報を抽出してここに提示しました。

于 2011-04-11T08:40:54.913 に答える