Beiträge durchsuchen

IEEE 754 Fließkommazahlen in Zeichen umwandeln

Manchmal braucht man als Embedded-Entwickler eine Möglichkeit, eine Fließkommazahl anzuzeigen (z.B. über den seriellen Port). Die meisten C(++)-Compiler, auch für Microcontroller, bringen „*printf“-ähnliche Anweisungen mit, aber dafür wird auch oft sehr viel Code mit eingebunden.

Das vorliegende C/C++ Fragment gibt eine FP-Zahl auf die Konsole aus. Der Code kann leicht für verschiedene Zwecke angepasst werden. Das Hauptaugenmerk ist hier auf die „union ieee754_float“ zu richten, die eine FP-Zahl auf „unsigned int“ abbildet. Es muss sichergestellt sein, das „unsigned int“ die gleiche Größe wie „float“ hat. Für „double“ ist hier entsprechend „unsigned long“ zu verwenden. Noch besser geeignet sind die Datentypen „uint32_t“ oder „uint64_t“, die über „#include <stdint.h>“ eingebunden werden können, sofern diese zur Verfügung stehen.

Der Code funktioniert in etwas so, wie die Fließkomma-Ausgabe der Microsoft-Basic Versionen aus den 1980’er Jahren. Einfach, und für die meisten Anwendungsfälle völlig ausreichend.

Für die interne Funktionsweise von Fließkommazahlen sei dieser Wikipedia-Artikel empfohlen:

https://de.wikipedia.org/wiki/Gleitkommazahl

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define MANTISSA_BITS          23
#define EXPONENT_BITS           8
#define EXPONENT_BIAS         128
#define FRACTAL_ZEROS           2
#define MANTISSA_DIGITS         7
#define EXPONENT_DIGITS         2
#define MANTISSA_VALUES   { 1000000U, 100000U, 10000U, 1000U, 100U, 10U, 1U }
#define FRACTIONAL_DIGITS (MANTISSA_DIGITS - 1)

union ieee754_float
{
    float f;

    /* This is the IEEE 754 single-precision format.  */
    struct
    {
        unsigned int mantissa : MANTISSA_BITS;
        unsigned int exponent : EXPONENT_BITS;
        unsigned int negative : 1;
    }
    ieee;
};

static void printDigits(unsigned int value, int point, 
                        int start, int count, bool removeZeros)
{
    static const unsigned int mantissaValues[] = MANTISSA_VALUES;
    const unsigned int *mv = &mantissaValues[start];
    while (count-- > 0)
    {
        char ch = '0';
        while (value >= *mv)
        {
            value -= *mv; ch++;
        }
        fputc(ch, stdout);
        point++;
        if (removeZeros && value == 0 && point >= 0)
        {
            break;
        }
        if (point == 0)
        {
            fputc('.', stdout);
        }
        mv++;
    }
}

static void printFloat(float f)
{
    union ieee754_float fbits;

    fbits.f = f;

    if (fbits.ieee.negative)
    {
        fputc('-', stdout);  fbits.ieee.negative = 0;
    }
    else
    {
        fputc(' ', stdout);
    }
    if (fbits.ieee.exponent == 0)
    {
        fputc('0', stdout);
    }
    else
    {
        int dp = 0;
        if (fbits.f < 1)
        {
            fbits.f *= 10000000.0F; dp = MANTISSA_DIGITS;
        }
        for (;;)
        {
            if (fbits.f > 9999999.9F)
            {
                fbits.f /= 10; dp--;
            }
            else if (fbits.f <= 999999.9F)
            {
                fbits.f *= 10; dp++;
            }
            else
            {
                fbits.f = floorf(fbits.f + 0.5F);
                break;
            }
        }
        int exp10, point;
        if (dp >= 0 && dp <= (MANTISSA_DIGITS + FRACTAL_ZEROS))
        {
            exp10 = 0;
            point = dp - MANTISSA_DIGITS;
        }
        else
        {
            exp10 = FRACTIONAL_DIGITS - dp;
            point = -1;
        }
        if (point >= 0)
        {
            fputc('0', stdout);
            fputc('.', stdout);
        }
        while (point >= 1)
        {
            fputc('0', stdout); point--;
        }

        unsigned int m = fbits.ieee.mantissa | (1 << MANTISSA_BITS);

        unsigned int shifts = MANTISSA_BITS - fbits.ieee.exponent + EXPONENT_BIAS;

        assert(shifts > 0);

        while (--shifts > 0)
        {
            m >>= 1;
        }
        printDigits(m, point, 0, MANTISSA_DIGITS, true);
        if (exp10)
        {
            fputc('E', stdout);
            if (exp10 < 0)
            {
                fputc('-', stdout);  exp10 = -exp10;
            }
            else
            {
                fputc('+', stdout);
            }
            printDigits((unsigned int)exp10, 0, 
                        MANTISSA_DIGITS - EXPONENT_DIGITS, EXPONENT_DIGITS, false);
        }
    }
}

static void compareFloat(float f)
{
    printf("%.7E\t", f);
    printFloat(f);
    fputc('\n', stdout);
}

int main(int argc, char *argv[])
{
    compareFloat(-3.141E-10F);
    compareFloat( 3.141E-9F);
    compareFloat(-3.141E-8F);
    compareFloat( 3.141E-7F);
    compareFloat(-3.141E-6F);
    compareFloat( 3.141E-5F);
    compareFloat(-3.141E-4F);
    compareFloat( 3.141E-3F);
    compareFloat(-3.141E-2F);
    compareFloat( 3.141E-1F);
    compareFloat(-3.141E-0F);
    compareFloat( 3.141E+1F);
    compareFloat(-3.141E+2F);
    compareFloat( 3.141E+3F);
    compareFloat(-3.141E+4F);
    compareFloat( 3.141E+5F);
    compareFloat(-3.141E+6F);
    compareFloat( 3.141E+7F);
    compareFloat(-3.141E+8F);
    compareFloat( 3.141E+9F);
    compareFloat(-3.141E+10F);

    getchar();
}

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.