2013-08-04 2 views
0

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

Я очень новичок в 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

Там есть аналогичный вопрос здесь: http://stackoverflow.com/questions/3316130/changing-float-type-to-short-but-with-same-behaviour-as-float- type-variable –

+0

Попробуйте преобразовать 0,8 в двоичный код и использовать только количество бит, которое вы имеете для хранения. чем пытаться преобразовать его обратно в десятичное и увидеть результат. Если у вас есть только 2 десятичных бита, вы можете использовать только 1/2 и 1/4, тем самым пытаясь сохранить, например. .8 будет отображаться как 1/2 + 1/4 = .75, это ближе к .8, чем к 1, но все же у вас есть то, что вы называете 'offset' – ted

ответ

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; 
}; 
-1

Последнее решение не так ... К сожалению ...

Попробуйте изменить экспонат на подписанный ... Он работал здесь.

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

+0

. После модификаций вы предложили все' Half 'decode to 0 .. . –

+0

Используйте приведенное выше предложение. – rbelli

+0

Я не думаю, что я понимаю - экспонент не может быть отрицательным ... –