I've managed to greatly simplify the FPN addition code, still same bug but now the code is far easier to read, it seems adding that extra variable to the shift functions was the greatest mistake I've made in the ALU project.
A reminder of the bug:
Code:
make check.run
...
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Running suite(s): ALU
../src/alur.c:366: alur_add() Error 0x00000022 34 'Numerical result out of range'
../tests/check_alu.c:1037: test_alur_add_floating_fn() _num = 1, _val = 1, (EXP.upto = 63) - (EXP.from = 52) = 11, (MAN.upto = 52) - (MAN.from = 0) = 52
../tests/check_alu.c:1048: test_alur_add_floating_fn() NUM = 0 +0000 01111111111 0000000000000000000000000000000000000000000000000000 // Incorrect result from software
../tests/check_alu.c:1050: test_alur_add_floating_fn() NUM = 0 +0001 10000000000 0000000000000000000000000000000000000000000000000000 // Correct result from hardware
99%: Checks: 1584, Failures: 1, Errors: 0
...
Compilation finished successfully.
The addition part:
Code:
int_t alur__add(
alu_t *alu
, alur_t NUM
, alur_t VAL
, uint_t cpy
, uint_t tmp
)
{
if ( alu )
{
int ret;
//alub_t n, v;
alup_t _NUM, _VAL;
NUM.node %= alu_used( alu );
VAL.node %= alu_used( alu );
cpy %= alu_used(alu);
tmp %= alu_used(alu);
ret = IFTRUE( !NUM.node || !VAL.node || !cpy || !tmp, EINVAL );
if ( ret )
{
alu_error( ret );
if ( !NUM.node ) alu_puts( "NUM.node was 0!" );
if ( !VAL.node ) alu_puts( "VAL.node was 0!" );
if ( !cpy ) alu_puts( "cpy was 0!" );
if ( !tmp ) alu_puts( "tmp was 0!" );
return ret;
}
alup_init_register( alu, _NUM, NUM );
alup_init_register( alu, _VAL, VAL );
if ( alur_floating( NUM ) || alur_floating( VAL ) )
{
alup_t _CPY, _TMP, _CMAN, _TMAN;
size_t exp;//, cexp, texp;//, bias;
bool_t truncated = false;
alup_init_floating( _CPY, alu_data(alu, cpy), alu_Nsize(alu) );
alup_init_floating( _TMP, alu_data(alu, tmp), alu_Nsize(alu) );
alup_init_mantissa( _CPY, _CMAN );
alup_init_mantissa( _TMP, _TMAN );
alup_mov( _CPY, _NUM );
alup_mov( _TMP, _VAL );
//cexp = alup_get_exponent( _CPY );
//texp = alup_get_exponent( _TMP );
//bias = alup_get_exponent_bias( _CPY );
ret = alup_match_exponents
(
_CPY.data
, _TMP.data
, alu_Nsize(alu)
);
truncated = (ret == ERANGE);
exp = alup_get_exponent( _CPY );
ret = alup__add_int2int( _CMAN, _TMAN );
if ( ret == EOVERFLOW ) ++exp;
(void)alup_set_exponent( _CPY, exp );
ret = alup_mov( _NUM, _CPY );
return IFTRUE( truncated || ret == ERANGE, ERANGE );
}
return alup__add_int2int( _NUM, _VAL );
}
return alu_err_null_ptr("alu");
}
Just in case anyone needs to see it, the exponent matching part:
Code:
int_t alup_match_exponents( void *_num, void *_val, size_t size )
{
int_t ret;
alup_t _NUM, _VAL, _DST, _MAN = {0};
size_t exp = 0, nexp, vexp, diff = 0;
bool truncated = false;
alup_init_floating( _NUM, _num, size );
alup_init_floating( _VAL, _val, size );
nexp = alup_get_exponent( _NUM );
vexp = alup_get_exponent( _VAL );
if ( nexp > vexp )
{
exp = nexp;
diff = nexp - vexp;
_DST = _VAL;
}
else if ( vexp > nexp )
{
exp = vexp;
diff = vexp - nexp;
_DST = _NUM;
}
if ( diff )
{
alup_init_mantissa( _DST, _MAN );
/* Match exponent and align mantissa */
(void)alup_set_exponent( _DST, exp );
ret = alup__shr( _MAN, diff );
truncated = (ret == ERANGE);
/* Insert assumed bit back into position */
if ( diff < _MAN.upto )
{
alub_t m = alub( _MAN.data, _MAN.upto - diff );
*(m.ptr) |= m.mask;
}
}
return IFTRUE( truncated, ERANGE );
}