реализация — Реализация числа с плавающей запятой половинной точности в переполнении стека

Я пытаюсь реализовать простой тип с плавающей точкой половинной точности, целиком для целей хранения (без арифметики, неявно преобразуется в удвоение), но я получаю странное поведение. Я получаю совершенно неверные значения для Half от -0,5 до 0,5. Также я получаю неприятное «смещение» для значений, например, 0,8 декодируется как 0,7998.

Я очень плохо знаком с C ++, поэтому было бы здорово, если бы вы указали на мою ошибку и помогли мне немного улучшить точность. Мне также любопытно, насколько портативно это решение. Спасибо!

Вот результат — двойное значение и фактическое декодированное значение от половины:

-1 -1
-0.9 -0.899902
-0.8 -0.799805
-0.7 -0.699951
-0.6 -0.599854
-0.5 -0.5
-0.4 -26208
-0.3 -19656
-0.2 -13104
-0.1 -6552
-1.38778e-16 -2560
0.1 6552
0.2 13104
0.3 19656
0.4 26208
0.5 32760
0.6 0.599854
0.7 0.699951
0.8 0.799805
0.9 0.899902

Вот код на данный момент:

#include <stdint.h>
#include <cmath>
#include <iostream>

using namespace std;

#define EXP 4
#define SIG 11

double normalizeS(uint v) {
return (0.5f * v / 2048 + 0.5f);
}

uint normalizeP(double v) {
return (uint)(2048 * (v - 0.5f) / 0.5f);
}

class Half {

struct Data {
unsigned short sign : 1;
unsigned short exponent : EXP;
unsigned short significant : SIG;
};

public:
Half() {}
Half(double d) { loadFromFloat(d); }

Half & operator = (long double d) {
loadFromFloat(d);
return *this;
}

operator double() {
long double sig = normalizeS(_d.significant);
if (_d.sign) sig = -sig;
return ldexp(sig, _d.exponent /*+ 1*/);
}

private:
void loadFromFloat(long double f) {
long double v;
int exp;
v = frexp(f, &exp);
v < 0 ? _d.sign = 1 : _d.sign = 0;
_d.exponent = exp/* - 1*/;
_d.significant = normalizeP(fabs(v));
}

Data _d;
};

int main() {

Half a[255];

double d = -1;

for (int i = 0; i < 20; ++i) {
a[i] = d;
cout << d << " " << a[i] << endl;
d += 0.1;
}
}

0

Решение

В итоге я получил очень простое (действительно наивное) решение, способное представлять каждое значение в нужном мне диапазоне: 0–64 с точностью 0,001.

Поскольку идея заключается в том, чтобы использовать его для хранения, это на самом деле лучше, потому что это позволяет преобразование из и в double без потери разрешения. Это также быстрее. Фактически он теряет некоторое разрешение (менее 16 бит) во имя более хорошего минимального шага, поэтому он может представлять любое из входных значений без аппроксимации — поэтому в этом случае LESS — БОЛЬШЕ. Использование полного разрешения 2 ^ 10 для плавающего компонента приведет к странному шагу, который не может точно представлять десятичные значения.

class Half {
public:
Half() {}
Half(const double d) { load(d); }
operator double() const { return _d.i + ((double)_d.f / 1000); }
private:
struct Data {
unsigned short i : 6;
unsigned short f : 10;
};
void load(const double d) {
int i = d;
_d.i = i;
_d.f = round((d - i) * 1000);
}
Data _d;
};
0

Другие решения

Последнее решение неверно … Извините …

Попробуйте сменить экспозицию на подписанную … Это сработало здесь.

Проблема заключается в том, что когда экспоненты становятся отрицательными, когда значение < 0,5 вы сохраняете экспозицию как положительное число, это проблема, которая делает число большим, когда abs (val)<0,5.

-1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector