arm gcc none eabi: Wie wird ein float/double gespeichert

Gast #3786219
Lesenswert?

Hallo Leute. Ich will einen float Wert zu einem anderen µC übertragen. 
Ich hatte vor den float auf meinem stm32 byteweise auf den anderen 
controller zu schieben. Soweit kein Problem. Das problem ist, der 
empfangende Controller ist kein STm32/ kein ARM!!. Somit muss ich wissen 
nach welchem schema der Compiler den Float abspeichert. Weiß jemand was?


Ps: Wie ein Double gespeichert wird würde mich auch interessieren :)
Gast #3786419
Lesenswert?

M. H. schrieb:
> Somit muss ich wissen
> nach welchem schema der Compiler den Float abspeichert. Weiß jemand was?

Guck's dir einfach an. Leg dir ein float Variable in den Speicher, setzt 
sie auf 1.0 und dann guck dir an, was im Speicher passiert, wenn du sie 
in einer Schleife immer weiter durch 2 teilst bzw. mit 2 multiplizierst. 
Damit siehst du schnell wie der Exponent incl. Vorzeichen liegt. Und 
dann ist der Rest einfach die Mantisse. Wegen der Normalisierung ist das 
erste Bit langweiig und wird meist weg gelassen.
#3786432
Lesenswert?

Die einzig wirklich robuste und portable Option wäre es wohl die float 
bzw. double Zahl in einen String (oder ein anderes "standardisiertes" 
Format) zu bringen, und anschließend dieses zu übertragen. Das ist zwar 
mit Overhead verbunden (printf, scanf, usw.), dafür aber unabhängig von 
der verwendeten Hardware und verschiedenen Software Implementierungen.

Wie kommunizieren die beiden Mikrocontroller denn eigentlich? UART? SPI? 
I2C?

Mit freundlichen Grüßen,
Karol Babioch
Persönliche Seite #3821083
Lesenswert?

Eine exakte Serialisierung ohne printf bekommt man fast schon mit 
Standardmitteln hin:
 
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <inttypes.h>
4
#include <math.h>
5

6
// double = sign * mant * 2 ^ (ex - __DBL_MANT_DIG__)
7
typedef struct
8
{
9
    int sign, ex;
10
    uint64_t mant;
11
    char s_mant[2 + __DBL_MANT_DIG__ /* GCC */];
12
} double_t;
13

14
void d2d (double x, double_t *d)
15
{
16
    uint64_t mant = 0;
17
    d->sign = copysign (1.0, x) < 0.0 ? -1 : 1;
18
    double r = frexp (copysign (x, 1.0), & d->ex);
19
    char *s = d->s_mant;
20
    
21
    *s++ = '.';
22
    for (int i = 0; i < __DBL_MANT_DIG__ /* GCC */; i++)
23
    {
24
        r = ldexp (r, 1);
25
        unsigned b = r >= 1.0;
26
        r -= b;
27
        mant = (mant << 1) | b;
28
        *s++ = '0' + b;
29
    }
30
    *s++ = '\0';
31
    d->mant = mant;
32
}
33

34

35
int main()
36
{
37
    int mdig = __DBL_MANT_DIG__; // GCC
38
    double x = -1.000000000000001;
39
    double_t d;
40
    for (int i = 0; i < 10; i++)
41
    {
42
        d2d (x, &d);
43
        printf ("%.16f = %c0b0%s * 2^%d\n", x,
44
                d.sign > 0 ? '+' : '-', d.s_mant, d.ex);
45
        x = nextafter (x, HUGE_VAL);
46
    }
47

48
    return EXIT_SUCCESS;
49
}

Gibt:
 
1
-1.0000000000000011 = -0b0.10000000000000000000000000000000000000000000000000101 * 2^1
2
-1.0000000000000009 = -0b0.10000000000000000000000000000000000000000000000000100 * 2^1
3
-1.0000000000000007 = -0b0.10000000000000000000000000000000000000000000000000011 * 2^1
4
-1.0000000000000004 = -0b0.10000000000000000000000000000000000000000000000000010 * 2^1
5
-1.0000000000000002 = -0b0.10000000000000000000000000000000000000000000000000001 * 2^1
6
-1.0000000000000000 = -0b0.10000000000000000000000000000000000000000000000000000 * 2^1
7
-0.9999999999999999 = -0b0.11111111111111111111111111111111111111111111111111111 * 2^0
8
-0.9999999999999998 = -0b0.11111111111111111111111111111111111111111111111111110 * 2^0
9
-0.9999999999999997 = -0b0.11111111111111111111111111111111111111111111111111101 * 2^0
10
-0.9999999999999996 = -0b0.11111111111111111111111111111111111111111111111111100 * 2^0

Wie man sieht zählt die Mantisse in jedem Schritt brav um 1 LSB hoch. 
Hinzu kommt dann noch Behandlung von NaN, Inf, Denormals, +/-0, etc.

Wem die Schleife zu unbequem ist oder keine String-Darstellung braucht 
kann auch einfach
 
1
d->mant = ldexp (r, __DBL_MANT_DIG__);

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren