Forum: Mikrocontroller und Digitale Elektronik arm gcc none eabi: Wie wird ein float/double gespeichert


von M. Н. (Gast)


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 :)

von (prx) A. K. (prx)


Lesenswert?

Gut, zu wissen was es alles nicht ist. Aber was ist es?

von M. Н. (Gast)


Lesenswert?

Es ist ein dsPIC

von holger (Gast)


Lesenswert?

>Somit muss ich wissen
>nach welchem schema der Compiler den Float abspeichert.

Nimm deinen Debugger, speichere 1.2345f irgendwo als
volatile float ab und vergleiche den Inhalt im Speicher.

von (prx) A. K. (prx)


Lesenswert?

Da der keine FPU hat hängt das vom Compiler ab. Dessen Handbuch wird es 
wissen. ARM hat IEEE754 Format, STM32 little endian.

von M. Н. (Gast)


Lesenswert?

A. K. schrieb:
> Da der keine FPU hat hängt das vom Compiler ab.


Der Stm32f4 hat eine FPU!

A. K. schrieb:
> ARM hat IEEE754 Format, STM32 little endian.

Der STM32 ist ein ARM??!?!?!?!?!

von holger (Gast)


Lesenswert?

>Der Stm32f4 hat eine FPU!

Der dsPic aber nicht. Also nochmal meinen Post oben
lesen, dann weisst du ganz genau ob die beiden zusammenpassen.

von M. Н. (Gast)


Lesenswert?

holger schrieb:
> Der dsPic aber nicht. Also nochmal meinen Post oben
> lesen, dann weisst du ganz genau ob die beiden zusammenpassen.

Ok. Dann lese ich mich mal in IEEE754 ein.

Den dsPIC hab ich schon :) bzw. Weiß wo ich die Info herbekomm

Danke

von holger (Gast)


Lesenswert?

>Der Stm32f4 hat eine FPU!

Double kann die übrigends nicht. Das wird in Software gemacht.

Jetzt frage ich mich nur noch weshalb man ein float
von einem Zahlenmonster zum anderen verschicken will.

von (prx) A. K. (prx)


Lesenswert?

holger schrieb:
> Double kann die übrigends nicht. Das wird in Software gemacht.

Spielt in dieser Frage keine Rolle, da das Format von ARM im ABI 
festgelegt sein dürfte, unabhängig davon, wie es implementiert wird. Nur 
die Byteorder ist dann vom exakten ARM abhängig.

von (prx) A. K. (prx)


Lesenswert?

M. H. schrieb:
> Der STM32 ist ein ARM??!?!?!?!?!

Die STM32 haben Cortex-M Cores, und die sind von ARM.

von (prx) A. K. (prx)


Lesenswert?

M. H. schrieb:
> Den dsPIC hab ich schon :) bzw. Weiß wo ich die Info herbekomm

Doku vom Compiler vermutlich. Evtl. ABI Doku, falls es die vom Microchip 
separat gibt. Von ARM gibt es die.

: Bearbeitet durch User
von M. Н. (Gast)


Lesenswert?

A. K. schrieb:
> M. H. schrieb:
>> Der STM32 ist ein ARM??!?!?!?!?!
>
> Die STM32 haben Cortex-M Cores, und die sind von ARM.

Das weiß ich auch.. Ich glaube wenn man das nicht wüsste siehts schlecht 
aus mitm Programmieren :)

Ich hab nur den Kontext der Aussage verramscht. Mein Satz war eine 
Feststellung, mit verwirrtem Hintergrund

von Mike (Gast)


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.

von Karol B. (johnpatcher)


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

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


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__);

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.