summaryrefslogtreecommitdiffhomepage
path: root/ir/tv
diff options
context:
space:
mode:
authorMatthias Braun <matze@braunis.de>2014-12-30 06:49:01 +0100
committerMatthias Braun <matze@braunis.de>2014-12-30 11:09:08 +0100
commitae6fff967c40dbb00a863c5f10e1fbb80fe06183 (patch)
treee9b32e697d0403e52a6e7944cd6c234a8b896af6 /ir/tv
parentbdbdf62bc5192f9672c0d5ab83d1a760be980cec (diff)
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.
Diffstat (limited to 'ir/tv')
-rw-r--r--ir/tv/fltcalc.c70
-rw-r--r--ir/tv/fltcalc.h5
-rw-r--r--ir/tv/tv.c33
3 files changed, 68 insertions, 40 deletions
diff --git a/ir/tv/fltcalc.c b/ir/tv/fltcalc.c
index 3e3bb3c..4c26727 100644
--- a/ir/tv/fltcalc.c
+++ b/ir/tv/fltcalc.c
@@ -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;
}
diff --git a/ir/tv/fltcalc.h b/ir/tv/fltcalc.h
index 89dd207..ccbb4ac 100644
--- a/ir/tv/fltcalc.h
+++ b/ir/tv/fltcalc.h
@@ -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);
diff --git a/ir/tv/tv.c b/ir/tv/tv.c
index adb3e75..ab24c8c 100644
--- a/ir/tv/tv.c
+++ b/ir/tv/tv.c
@@ -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))