Commit 70862779 authored by Manuel Mohr's avatar Manuel Mohr
Browse files

tv/fltcalc: Create fp_values with long double info and convert later.

fc_val_from_ieee754 simply does not work correctly for denormalized
input values if the desired result is smaller than a long double.
However, converting to an fp_value using the mantissa/characteristic
information of a long double and later using fc_cast to convert to
the desired target format works.
parent 683378aa
......@@ -15,6 +15,7 @@
#include <math.h>
#include <inttypes.h>
#include <float.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
......@@ -119,6 +120,8 @@ static int max_precision;
/** Exact flag. */
static int fc_exact = 1;
static float_descriptor_t long_double_desc;
/** pack machine-like */
static void *pack(const fp_value *int_float, void *packed)
{
......@@ -765,58 +768,45 @@ int fc_get_buffer_length(void)
return calc_buffer_size;
}
void *fc_val_from_str(const char *str, size_t len,
const float_descriptor_t *desc, void *result)
void *fc_val_from_str(const char *str, size_t len, void *result)
{
char *buffer;
/* XXX excuse of an implementation to make things work */
long double val;
fp_value *tmp = (fp_value*) alloca(calc_buffer_size);
float_descriptor_t tmp_desc;
buffer = (char*) alloca(len+1);
char *buffer = alloca(len + 1);
memcpy(buffer, str, len);
buffer[len] = '\0';
val = string_to_long_double(buffer);
tmp_desc.exponent_size = 15;
tmp_desc.mantissa_size = 63;
tmp_desc.explicit_one = 1;
fc_val_from_ieee754(val, &tmp_desc, tmp);
return fc_cast(tmp, desc, (fp_value*) result);
long double val = string_to_long_double(buffer);
return fc_val_from_ieee754(val, result);
}
fp_value *fc_val_from_ieee754(long double l, const float_descriptor_t *desc,
fp_value *result)
fp_value *fc_val_from_ieee754(long double l, fp_value *result)
{
char *temp;
int bias_res, bias_val, mant_val;
value_t srcval;
char sign;
uint32_t exponent, mantissa0, mantissa1;
size_t long_double_size = sizeof(long double);
srcval.d = l;
bias_res = ((1 << (desc->exponent_size - 1)) - 1);
bias_res = ((1 << (long_double_desc.exponent_size - 1)) - 1);
if (long_double_size == 8) {
if (long_double_desc.exponent_size == 11 && long_double_desc.mantissa_size == 52) {
assert(sizeof(long double) == 8);
mant_val = 52;
bias_val = 0x3ff;
sign = (srcval.val_ld8.high & 0x80000000) != 0;
exponent = (srcval.val_ld8.high & 0x7FF00000) >> 20;
mantissa0 = srcval.val_ld8.high & 0x000FFFFF;
mantissa1 = srcval.val_ld8.low;
} else {
} else if (long_double_desc.exponent_size == 15 && long_double_desc.mantissa_size == 63) {
/* we assume an x86-like 80bit representation of the value... */
assert(sizeof(long double)==12 || sizeof(long double)==16);
assert(sizeof(long double) == 12 || sizeof(long double) == 16);
mant_val = 63;
bias_val = 0x3fff;
sign = (srcval.val_ld12.high & 0x00008000) != 0;
exponent = (srcval.val_ld12.high & 0x00007FFF) ;
mantissa0 = srcval.val_ld12.mid;
mantissa1 = srcval.val_ld12.low;
} else {
panic("unsupported long double format");
}
if (result == NULL)
......@@ -826,7 +816,7 @@ fp_value *fc_val_from_ieee754(long double l, const float_descriptor_t *desc,
/* CLEAR the buffer, else some bits might be uninitialized */
memset(result, 0, fc_get_buffer_length());
result->desc = *desc;
result->desc = long_double_desc;
result->clss = FC_NORMAL;
result->sign = sign;
......@@ -844,7 +834,7 @@ fp_value *fc_val_from_ieee754(long double l, const float_descriptor_t *desc,
* this looks more complicated than it is: unbiased input exponent + output bias,
* minus the mantissa difference which is added again later when the output float
* becomes normalized */
sc_val_from_long((exponent - bias_val + bias_res) - (mant_val - desc->mantissa_size), _exp(result));
sc_val_from_long((exponent - bias_val + bias_res) - (mant_val - long_double_desc.mantissa_size), _exp(result));
/* build mantissa representation */
if (exponent != 0) {
......@@ -895,25 +885,13 @@ long double fc_val_to_ieee754(const fp_value *val)
uint32_t mantissa0;
uint32_t mantissa1;
value_t buildval;
float_descriptor_t desc;
unsigned mantissa_size;
value_t buildval;
unsigned mantissa_size;
size_t long_double_size = sizeof(long double);
if (long_double_size == 8) {
desc.exponent_size = 11;
desc.mantissa_size = 52;
desc.explicit_one = 0;
} else {
desc.exponent_size = 15;
desc.mantissa_size = 63;
desc.explicit_one = 1;
}
mantissa_size = desc.mantissa_size + desc.explicit_one;
mantissa_size = long_double_desc.mantissa_size + long_double_desc.explicit_one;
temp = (fp_value*) alloca(calc_buffer_size);
value = fc_cast(val, &desc, temp);
value = fc_cast(val, &long_double_desc, temp);
sign = value->sign;
......@@ -930,20 +908,25 @@ long double fc_val_to_ieee754(const fp_value *val)
for (byte_offset = 0; byte_offset < 4; byte_offset++)
mantissa1 |= sc_sub_bits(_mant(value), mantissa_size, byte_offset) << (byte_offset << 3);
for (; (byte_offset<<3) < desc.mantissa_size; byte_offset++)
for (; (byte_offset<<3) < long_double_desc.mantissa_size; byte_offset++)
mantissa0 |= sc_sub_bits(_mant(value), mantissa_size, byte_offset) << ((byte_offset - 4) << 3);
if (long_double_size == 8) {
if (long_double_desc.exponent_size == 11 && long_double_desc.mantissa_size == 52) {
assert(sizeof(long double) == 8);
mantissa0 &= 0x000FFFFF; /* get rid of garbage */
buildval.val_ld8.high = sign << 31;
buildval.val_ld8.high |= exponent << 20;
buildval.val_ld8.high |= mantissa0;
buildval.val_ld8.low = mantissa1;
} else {
} else if (long_double_desc.exponent_size == 15 && long_double_desc.mantissa_size == 63) {
/* we assume an x86-like 80bit representation of the value... */
assert(sizeof(long double) == 12 || sizeof(long double) == 16);
buildval.val_ld12.high = sign << 15;
buildval.val_ld12.high |= exponent;
buildval.val_ld12.mid = mantissa0;
buildval.val_ld12.low = mantissa1;
} else {
panic("unsupported long double format");
}
return buildval.d;
......@@ -955,7 +938,9 @@ fp_value *fc_cast(const fp_value *value, const float_descriptor_t *desc,
char *temp;
int exp_offset, val_bias, res_bias;
if (result == NULL) result = calc_buffer;
if (result == NULL)
result = calc_buffer;
assert(value != result);
temp = (char*) alloca(value_size);
if (value->desc.exponent_size == desc->exponent_size &&
......@@ -1307,6 +1292,17 @@ void init_fltcalc(int precision)
calc_buffer = (fp_value*) xmalloc(calc_buffer_size);
memset(calc_buffer, 0, calc_buffer_size);
const size_t long_double_size = sizeof(long double);
#if LDBL_MANT_DIG == 64
assert(long_double_size == 12 || long_double_size == 16);
long_double_desc = (float_descriptor_t) { 15, 63, 1 };
#elif LDBL_MANT_DIG == 53
assert(long_double_size == 8);
long_double_desc = (float_descriptor_t) { 11, 52, 0 };
#else
#error "Unsupported long double format"
#endif
}
}
......
......@@ -58,17 +58,13 @@ const void *fc_get_buffer(void);
int fc_get_buffer_length(void);
/*}@*/
void *fc_val_from_str(const char *str, size_t len, const float_descriptor_t *desc, void *result);
void *fc_val_from_str(const char *str, size_t len, void *result);
/** get the representation of a floating point value
* This function tries to builds a representation having the same value as the
* float number passed.
* If the wished precision is less than the precision of long double the value
* built will be rounded. Therefore only an approximation of the passed float
* can be expected in this case.
* long double floating point number passed.
*
* @param l The floating point number to build a representation for
* @param desc The floating point descriptor
* @param result A buffer to hold the value built. If this is NULL, the internal
* accumulator buffer is used. Note that the buffer must be big
* enough to hold the value. Use fc_get_buffer_length() to find out
......@@ -77,8 +73,7 @@ void *fc_val_from_str(const char *str, size_t len, const float_descriptor_t *des
* @return The result pointer passed to the function. If this was NULL this returns
* a pointer to the internal accumulator buffer
*/
fp_value *fc_val_from_ieee754(long double l, const float_descriptor_t *desc,
fp_value *result);
fp_value *fc_val_from_ieee754(long double l, fp_value *result);
/** retrieve the float value of an internal value
* This function casts the internal value to long double and returns a
......
......@@ -247,6 +247,16 @@ static const float_descriptor_t *get_descriptor(const ir_mode *mode)
return &mode->float_desc;
}
static ir_tarval *get_tarval_from_fp_value(fp_value *val, ir_mode *mode)
{
const float_descriptor_t *desc = get_descriptor(mode);
const int buffer_length = fc_get_buffer_length();
fp_value *tmp = alloca(buffer_length);
memcpy(tmp, val, buffer_length);
fp_value *casted_val = fc_cast(tmp, desc, NULL);
return get_tarval(casted_val, buffer_length, mode);
}
ir_tarval *new_integer_tarval_from_str(const char *str, size_t len, char sign,
unsigned char base, ir_mode *mode)
{
......@@ -319,8 +329,6 @@ static ir_tarval *new_tarval_from_str_int(const char *str, size_t len,
ir_tarval *new_tarval_from_str(const char *str, size_t len, ir_mode *mode)
{
const float_descriptor_t *desc;
assert(str);
assert(len);
assert(mode);
......@@ -336,11 +344,10 @@ ir_tarval *new_tarval_from_str(const char *str, size_t len, ir_mode *mode)
/* XXX This is C semantics */
return atoi(str) ? tarval_b_true : tarval_b_false;
case irms_float_number:
desc = get_descriptor(mode);
fc_val_from_str(str, len, desc, NULL);
return get_tarval(fc_get_buffer(), fc_get_buffer_length(), mode);
case irms_float_number: {
fp_value *val = fc_val_from_str(str, len, NULL);
return get_tarval_from_fp_value(val, mode);
}
case irms_reference:
if (!strcasecmp(str, "null"))
return get_tarval_null(mode);
......@@ -421,12 +428,9 @@ uint64_t get_tarval_uint64(ir_tarval *tv)
ir_tarval *new_tarval_from_long_double(long double d, ir_mode *mode)
{
const float_descriptor_t *desc;
assert(mode && (get_mode_sort(mode) == irms_float_number));
desc = get_descriptor(mode);
fc_val_from_ieee754(d, desc, NULL);
return get_tarval(fc_get_buffer(), fc_get_buffer_length(), mode);
fp_value *val = fc_val_from_ieee754(d, NULL);
return get_tarval_from_fp_value(val, mode);
}
ir_tarval *new_tarval_from_double(double d, ir_mode *mode)
......@@ -795,10 +799,9 @@ ir_tarval *tarval_convert_to(ir_tarval *src, ir_mode *dst_mode)
len = snprintf(buffer, 100, "%s",
sc_print(src->value, get_mode_size_bits(src->mode), SC_DEC, mode_is_signed(src->mode)));
buffer[100 - 1] = '\0';
desc = get_descriptor(dst_mode);
fc_val_from_str(buffer, len, desc, NULL);
return get_tarval(fc_get_buffer(), fc_get_buffer_length(), dst_mode);
fp_value *val = fc_val_from_str(buffer, len, NULL);
return get_tarval_from_fp_value(val, dst_mode);
default:
break;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment