Commit ae6fff96 authored by Matthias Braun's avatar Matthias Braun
Browse files

tv: rework NaN handling

- remove get_mode_nan() as there is not just a single NaN values but several
  different ones.
- introduce new_tarval_nan() which allows to construct signaling and
  quiet NaNs with payload.
- introduce tarval_is_snan() and tarval_is_qnan() to help unittests.
parent bdbdf62b
......@@ -194,15 +194,6 @@ FIRM_API ir_tarval *get_mode_all_one(const ir_mode *mode);
*/
FIRM_API ir_tarval *get_mode_infinite(const ir_mode *mode);
/**
* Returns a NAN value of a given mode.
*
* This is only valid for float_numbers, other modes will result in tarval_bad.
* There are typically multiple possible representations of NaN, don't compare
* with this value but use tarval_is_NaN() instead.
*/
FIRM_API ir_tarval *get_mode_NAN(const ir_mode *mode);
FIRM_API ir_mode *mode_M; /**< memory */
FIRM_API ir_mode *mode_F; /**< ieee754 binary32 float (single precision) */
......
......@@ -149,6 +149,15 @@ FIRM_API ir_tarval *new_tarval_from_long(long l, ir_mode *mode);
FIRM_API ir_tarval *new_tarval_from_bytes(unsigned char const *buf,
ir_mode *mode);
/**
* Construct a new floating point quiet NaN value.
* @param mode floating point mode for the resulting value
* @param signaling if != 0 produces a signaling NaN else a quiet one.
* @param payload if != NULL puts the integer tarval into the mantissa.
*/
FIRM_API ir_tarval *new_tarval_nan(ir_mode *mode, int signaling,
ir_tarval *payload);
/**
* Write tarval to a sequence of bytes. The value is written in a
* "little endian" fashion which means the less significant bytes come first.
......@@ -644,12 +653,26 @@ FIRM_API int tarval_ieee754_can_conv_lossless(const ir_tarval *tv, ir_mode *mode
FIRM_API unsigned tarval_ieee754_get_exact(void);
/**
* Check if its the a floating point NaN.
* Check if @P tv is a floating point NaN.
*
* @param tv the tarval
*/
FIRM_API int tarval_is_nan(const ir_tarval *tv);
/**
* Check if @p tv is a floating point quiet NaN.
*
* @param tv the tarval
*/
FIRM_API int tarval_is_quiet_nan(const ir_tarval *tv);
/**
* Check if @p tv is a floating point signaling NaN.
*
* @param tv the tarval
*/
FIRM_API int tarval_is_signaling_nan(const ir_tarval *tv);
/**
* Check if its the a floating point +inf.
*
......
......@@ -285,12 +285,6 @@ ir_tarval *get_mode_infinite(const ir_mode *mode)
return mode->infinity;
}
ir_tarval *get_mode_NAN(const ir_mode *mode)
{
assert(mode_is_float(mode));
return mode->nan;
}
int (mode_is_signed)(const ir_mode *mode)
{
return mode_is_signed_(mode);
......
......@@ -163,7 +163,6 @@ struct ir_mode {
ir_tarval *one; /**< the value 1 */
ir_tarval *all_one; /**< the value ~0 */
ir_tarval *infinity; /**< the infinity value */
ir_tarval *nan; /** the not a number (NaN) value */
ir_mode *eq_unsigned; /**< For pointer modes, the equivalent unsigned integer one. */
};
......
......@@ -287,6 +287,11 @@ return_nan_b:
return false;
}
static void fc_get_qnan(const float_descriptor_t *desc, fp_value *result)
{
fc_get_nan(desc, result, false, NULL);
}
/**
* calculate a + b, where a is the value with the bigger exponent
*/
......@@ -730,15 +735,14 @@ long double fc_val_to_ieee754(const fp_value *val)
return result;
}
static bool is_quiet_nan(const fp_value *value)
bool fc_nan_is_quiet(const fp_value *value)
{
assert(value->clss == FC_NAN);
/* most significant mantissa bit cleared means signaling, else quiet.
* This does not include the "explicit one" bit for 80bit x86. */
const float_descriptor_t *desc = &value->desc;
if (!desc->explicit_one) {
return sc_get_bit_at(_mant(value), desc->mantissa_size-1);
} else {
return !sc_get_bit_at(_mant(value), desc->mantissa_size-2);
}
unsigned bit = desc->mantissa_size + ROUNDING_BITS - 1 - desc->explicit_one;
return sc_get_bit_at(_mant(value), bit);
}
fp_value *fc_cast(const fp_value *value, const float_descriptor_t *dest,
......@@ -759,8 +763,7 @@ fp_value *fc_cast(const fp_value *value, const float_descriptor_t *dest,
if (value->clss == FC_NAN) {
/* TODO: preserve mantissa bits? */
return is_quiet_nan(value) ? fc_get_qnan(dest, result)
: fc_get_snan(dest, result);
return fc_get_nan(dest, result, !fc_nan_is_quiet(value), NULL);
} else if (value->clss == FC_INF) {
return fc_get_inf(dest, result, value->sign);
}
......@@ -841,7 +844,8 @@ fp_value *fc_get_epsilon(const float_descriptor_t *desc, fp_value *result)
return result;
}
fp_value *fc_get_snan(const float_descriptor_t *desc, fp_value *result)
fp_value *fc_get_nan(const float_descriptor_t *desc, fp_value *result,
bool signaling, sc_word *payload)
{
if (result == NULL)
result = calc_buffer;
......@@ -852,32 +856,32 @@ fp_value *fc_get_snan(const float_descriptor_t *desc, fp_value *result)
sc_max_from_bits(desc->exponent_size, 0, _exp(result));
/* signaling NaN has msb in mantissa cleared */
sc_zero(_mant(result));
/* we still set our explicit one */
if (desc->explicit_one) {
sc_set_bit_at(_mant(result), desc->mantissa_size+ROUNDING_BITS-1);
sc_set_bit_at(_mant(result), desc->mantissa_size+ROUNDING_BITS-3);
/* copy payload into mantissa */
unsigned mantissa_size = desc->mantissa_size;
bool explicit_one = desc->explicit_one;
if (payload != NULL) {
memcpy(_mant(result), payload, value_size);
/* Limit payload to mantissa size. The "explicit_one" on 80bit x86 must
* be 0 for NaNs. */
sc_zero_extend(_mant(result), mantissa_size - explicit_one);
/* Ensure that we have at least 1 bit set. */
if (sc_is_zero(_mant(result), mantissa_size))
sc_set_bit_at(_mant(result), ROUNDING_BITS);
/* Adjust for rounding bits. */
sc_shlI(_mant(result), ROUNDING_BITS, _mant(result));
} else {
sc_zero(_mant(result));
/* Ensure that we have at least 1 bit set. */
if (signaling)
sc_set_bit_at(_mant(result), ROUNDING_BITS);
}
return result;
}
fp_value *fc_get_qnan(const float_descriptor_t *desc, fp_value *result)
{
if (result == NULL)
result = calc_buffer;
result->desc = *desc;
result->clss = FC_NAN;
result->sign = 0;
sc_max_from_bits(desc->exponent_size, 0, _exp(result));
/* quiet NaN has the msb of the mantissa set, so shift one there */
sc_zero(_mant(result));
sc_set_bit_at(_mant(result), desc->mantissa_size+ROUNDING_BITS-1);
if (desc->explicit_one)
sc_set_bit_at(_mant(result), desc->mantissa_size+ROUNDING_BITS-2);
/* The most significant mantissa bit indicates whether the NaN is quiet. */
unsigned quiet_bit = mantissa_size + ROUNDING_BITS - 1 - explicit_one;
if (signaling)
sc_clear_bit_at(_mant(result), quiet_bit);
else
sc_set_bit_at(_mant(result), quiet_bit);
return result;
}
......
......@@ -122,8 +122,8 @@ fp_value *fc_cast(const fp_value *val, const float_descriptor_t *desc, fp_value
* a pointer to the internal accumulator buffer
*/
fp_value *fc_get_max(const float_descriptor_t *desc, fp_value *result, bool sign);
fp_value *fc_get_snan(const float_descriptor_t *desc, fp_value *result);
fp_value *fc_get_qnan(const float_descriptor_t *desc, fp_value *result);
fp_value *fc_get_nan(const float_descriptor_t *desc, fp_value *result,
bool signaling, sc_word *payload);
fp_value *fc_get_inf(const float_descriptor_t *desc, fp_value *result, bool sign);
fp_value *fc_get_small(const float_descriptor_t *desc, fp_value *result);
fp_value *fc_get_epsilon(const float_descriptor_t *desc, fp_value *result);
......@@ -133,6 +133,7 @@ bool fc_is_zero(const fp_value *a);
bool fc_is_negative(const fp_value *a);
bool fc_is_inf(const fp_value *a);
bool fc_is_nan(const fp_value *a);
bool fc_nan_is_quiet(const fp_value *a);
bool fc_is_subnormal(const fp_value *a);
fp_value *fc_add(const fp_value *a, const fp_value *b, fp_value *result);
......
......@@ -248,6 +248,20 @@ ir_tarval *new_tarval_from_long(long l, ir_mode *mode)
}
}
ir_tarval *new_tarval_nan(ir_mode *mode, int signaling, ir_tarval *payload)
{
assert(payload == NULL || get_mode_arithmetic(get_tarval_mode(payload))
== irma_twos_complement);
sc_word *sc_payload = payload != NULL ? payload->value : NULL;
assert(mode_is_float(mode));
unsigned buffer_len = fc_get_buffer_length();
fp_value *buffer = (fp_value*)ALLOCAN(char, buffer_len);
const float_descriptor_t *desc = get_descriptor(mode);
fc_get_nan(desc, buffer, signaling, sc_payload);
return get_tarval(buffer, buffer_len, mode);
}
ir_tarval *new_tarval_from_bytes(unsigned char const *buf,
ir_mode *mode)
{
......@@ -445,8 +459,6 @@ void init_mode_values(ir_mode* mode)
mode->all_one = tarval_bad;
fc_get_inf(desc, buf, false);
mode->infinity = get_tarval(buf, buflen, mode);
fc_get_qnan(desc, buf);
mode->nan = get_tarval(buf, buflen, mode);
fc_get_max(desc, buf, true); // min = negative maximum
mode->min = get_tarval(buf, buflen, mode);
fc_get_max(desc, buf, false);
......@@ -459,7 +471,6 @@ void init_mode_values(ir_mode* mode)
case irms_internal_boolean:
mode->all_one = tarval_b_true;
mode->infinity = tarval_bad;
mode->nan = tarval_bad;
mode->min = tarval_b_false;
mode->max = tarval_b_true;
mode->null = tarval_b_false;
......@@ -475,7 +486,6 @@ void init_mode_values(ir_mode* mode)
sc_max_from_bits(bits, false, buf);
mode->all_one = get_tarval(buf, buflen, mode);
mode->infinity = tarval_bad;
mode->nan = tarval_bad;
sc_min_from_bits(bits, sign, buf);
mode->min = get_tarval(buf, buflen, mode);
sc_max_from_bits(bits, sign, buf);
......@@ -495,7 +505,6 @@ void init_mode_values(ir_mode* mode)
mode->null = tarval_bad;
mode->one = tarval_bad;
mode->infinity = tarval_bad;
mode->nan = tarval_bad;
break;
}
}
......@@ -1333,6 +1342,20 @@ int tarval_is_nan(const ir_tarval *tv)
return fc_is_nan((const fp_value*) tv->value);
}
int tarval_is_quiet_nan(const ir_tarval *tv)
{
if (!tarval_is_nan(tv))
return false;
return fc_nan_is_quiet((const fp_value*)tv->value);
}
int tarval_is_signaling_nan(const ir_tarval *tv)
{
if (!tarval_is_nan(tv))
return false;
return !fc_nan_is_quiet((const fp_value*)tv->value);
}
int tarval_is_plus_inf(const ir_tarval *tv)
{
if (!mode_is_float(tv->mode))
......
......@@ -33,7 +33,8 @@ static void test_mode(ir_mode *mode)
if (mode_is_float(mode)) {
test_tv(get_mode_infinite(mode));
test_tv(tarval_neg(get_mode_infinite(mode)));
test_tv(get_mode_NAN(mode));
test_tv(new_tarval_nan(mode, true, NULL));
test_tv(new_tarval_nan(mode, false, NULL));
if (mode == mode_F) {
test_tv(new_tarval_from_double(FLT_MIN, mode));
test_tv(new_tarval_from_double(FLT_EPSILON, mode));
......
......@@ -46,7 +46,6 @@ int main(void)
test_float(FLT_MIN, NULL);
test_float(INFINITY, get_mode_infinite(mode_F));
test_float(HUGE_VALF, NULL);
test_float(NAN, get_mode_NAN(mode_F));
test_float(FLT_MIN, get_tarval_small(mode_F));
test_float(FLT_EPSILON, get_tarval_epsilon(mode_F));
test_float(FLT_MIN * FLT_EPSILON, NULL); // subnormal
......@@ -60,7 +59,6 @@ int main(void)
test_double(DBL_MIN, NULL);
test_double(HUGE_VAL, NULL);
test_double(INFINITY, get_mode_infinite(mode_D));
test_double(NAN, get_mode_NAN(mode_D));
test_double(DBL_MIN, get_tarval_small(mode_D));
test_double(DBL_EPSILON, get_tarval_epsilon(mode_D));
test_double(DBL_MIN * DBL_EPSILON, NULL); // subnormal
......@@ -84,7 +82,6 @@ int main(void)
test_ldouble(LDBL_MIN, NULL);
test_ldouble(HUGE_VAL, NULL);
test_ldouble(INFINITY, get_mode_infinite(mode_E));
test_ldouble(NAN, get_mode_NAN(mode_E));
test_ldouble(LDBL_MIN, get_tarval_small(mode_E));
test_ldouble(LDBL_EPSILON, get_tarval_epsilon(mode_E));
test_ldouble(LDBL_MIN * LDBL_EPSILON, NULL); // subnormal
......
......@@ -14,7 +14,8 @@ static void check_mode(ir_mode *mode)
ir_tarval *minus_zero = tarval_neg(zero);
ir_tarval *two = new_tarval_from_str("2", 1, mode);
ir_tarval *half = new_tarval_from_str("0.5", 3, mode);
ir_tarval *nan = get_mode_NAN(mode);
ir_tarval *qnan = new_tarval_nan(mode, false, NULL);
ir_tarval *snan = new_tarval_nan(mode, true, NULL);
ir_tarval *inf = get_mode_infinite(mode);
ir_tarval *minus_inf = tarval_neg(inf);
ir_tarval *small = get_tarval_small(mode);
......@@ -39,7 +40,8 @@ static void check_mode(ir_mode *mode)
inf,
minus_inf,
minus_one,
nan,
qnan,
snan,
denorm,
tarval_neg(denorm),
};
......@@ -64,8 +66,9 @@ static void check_mode(ir_mode *mode)
ir_tarval *mul = tarval_mul(val, zero);
assert((tarval_is_negative(val) && mul == minus_zero)
|| (!tarval_is_negative(val) && mul == zero)
|| (val == nan && mul == nan)
|| (val == inf && mul == nan)
|| (val == qnan && mul == qnan)
|| (val == snan && mul == snan)
|| (val == inf && tarval_is_nan(mul))
|| (val == minus_inf && tarval_is_nan(mul)));
assert(!tarval_is_finite(val) || tarval_sub(val, val, mode) == zero);
}
......@@ -98,13 +101,13 @@ static void check_mode(ir_mode *mode)
/* nan stays nan TODO: check nan payload? */
for (unsigned i = 0; i < ARRAY_SIZE(values); ++i) {
ir_tarval *val = values[i];
assert(tarval_is_nan(tarval_add(val, nan)));
assert(tarval_is_nan(tarval_sub(val, nan, mode)));
assert(tarval_is_nan(tarval_mul(val, nan)));
assert(tarval_is_nan(tarval_div(val, nan)));
assert(tarval_is_nan(tarval_add(val, qnan)));
assert(tarval_is_nan(tarval_sub(val, qnan, mode)));
assert(tarval_is_nan(tarval_mul(val, qnan)));
assert(tarval_is_nan(tarval_div(val, qnan)));
}
assert(tarval_is_nan(tarval_neg(nan)));
assert(tarval_is_nan(tarval_abs(nan)));
assert(tarval_is_nan(tarval_neg(qnan)));
assert(tarval_is_nan(tarval_abs(qnan)));
/* minus zero */
assert(tarval_mul(minus_zero, minus_zero) == zero);
......
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