RISC-V/V003/math/common/float.cpp
2024-05-07 11:46:49 +02:00

114 lines
2.8 KiB
C++

/* Reprezentace float podle IEEE 754 v little endien */
#include <stdint.h>
typedef __SIZE_TYPE__ size_t;
typedef union {
float f;
struct {
uint32_t frac : 23;
uint32_t exp : 8;
uint32_t sign : 1;
} u;
} float_unsigned;
typedef union {
uint32_t u;
struct {
uint32_t l : 28;
uint32_t h : 4;
} e;
} u2m;
// výstupní znaky
static const char * dec = "0123456789";
// decimální exponent e do bufferu buff
static size_t exp_str (char * buf, const int e) {
size_t n = 0;
int exp = 0, res;
buf [n++] = 'E';
if (e > 0) {
buf [n++] = '+';
exp = e;
} else if (e < 0) {
buf [n++] = '-';
exp = -e;
} else {
buf [n++] = '+';
buf [n++] = '0';
buf [n++] = '0';
return n;
}
n += 2;
res = exp % 10;
buf [n - 1] = dec [res];
exp = exp / 10;
res = exp % 10;
buf [n - 2] = dec [res];
return n;
}
// konstanty pro decimální normalizaci
static const float exp_plus [] = {
1.0e+1f, 1.0e+2f, 1.0e+4f, 1.0e+8f, 1.0e+16f, 1.0e+32f,
};
static const float exp_minus [] = {
1.0e-1f, 1.0e-2f, 1.0e-4f, 1.0e-8f, 1.0e-16f, 1.0e-32f,
};
// decimální normalizace f do rozsahu <0.1, 1.0)
static float f_norm (const float f, int * pe) {
float_unsigned res;
res.f = f;
uint32_t s = res.u.sign; // schovej znaménko
res.u.sign = 0u; // dál už počítej jen s absolutní hodnotou
unsigned n = 5u;
if (res.f >= 1.0f) {
do {
if (res.f >= exp_plus [n]) {
res.f *= exp_minus [n];
* pe += 1 << n;
}
} while (n--);
res.f *= 0.1f;
* pe += 1;
} else {
do {
if (res.f < exp_minus [n]) {
res.f *= exp_plus [n];
* pe -= 1 << n;
}
} while (n--);
}
res.u.sign = s; // obnov znaménko
// printf("normalized = %f, decimal exponent = %d\n", res.f, * pe);
return res.f;
}
static unsigned to_str (const float f, char * buf) {
int dec_exp = 0;
float_unsigned fu;
fu.f = f_norm (f, & dec_exp);
u2m um;
// převod formátu pro výstup číslic pomocí celočíselného násobení 10
um.u = (fu.u.frac | (1lu << 23)) << (fu.u.exp - 122u);
unsigned n = 0;
if (fu.u.sign) buf [n++] = '-';
else buf [n++] = '+';
if (fu.f != 0.0f) {
for (;;) { // exportuj decimální číslice
um.u *= 10u;
const char c = dec [um.e.h];
um.e.h = 0u;
if (n == 2) buf [n++] = '.';
buf [n++] = c;
if (n >= 10) break; // 8 platnych cislic
}
n += exp_str (buf + n, dec_exp - 1);
} else { // 0.0f
buf [n++] = '0';
for (unsigned i=0; i<12; i++) buf[n++] = ' ';
}
buf [n] = '\0'; // ukončení
return n;
}
#include "print.h"
Print & Print::operator<< (const float num) {
const unsigned n = to_str(num, buf);
BlockDown (buf, n);
return * this;
}