+
+ sign = _sign(value);
+
+ /* @@@ long double exponent is 15bit, so the use of sc_val_to_long should not
+ * lead to wrong results */
+ exponent = sc_val_to_long(_exp(value)) ;
+
+ sc_val_from_ulong(2, NULL);
+ _shift_right(_mant(value), sc_get_buffer(), _mant(value));
+
+ mantissa0 = 0;
+ mantissa1 = 0;
+
+ for (byte_offset = 0; byte_offset < 4; byte_offset++)
+ mantissa1 |= sc_sub_bits(_mant(value), result_mantissa, byte_offset) << (byte_offset<<3);
+
+ for (; (byte_offset<<3) < result_mantissa; byte_offset++)
+ mantissa0 |= sc_sub_bits(_mant(value), result_mantissa, byte_offset) << ((byte_offset-4)<<3);
+
+#ifndef HAVE_LONG_DOUBLE
+ mantissa0 &= 0x000FFFFF; /* get rid of garbage */
+#endif
+
+#ifdef HAVE_LONG_DOUBLE
+ buildval.val.high = sign << 15;
+ buildval.val.high |= exponent;
+ buildval.val.mid = mantissa0;
+ buildval.val.low = mantissa1;
+#else /* no long double */
+ buildval.val.high = sign << 31;
+ buildval.val.high |= exponent << 20;
+ buildval.val.high |= mantissa0;
+ buildval.val.low = mantissa1;
+#endif
+
+ TRACEPRINTF(("val_to_float: %d-%x-%x%x\n", sign, exponent, mantissa0, mantissa1));
+ return buildval.d;
+}
+
+char* fc_cast(const void *val, char exp_size, char mant_size, char *result)
+{
+ const char *value = (const char*) val;
+ char *temp;
+ int exp_offset, val_bias, res_bias;
+
+ if (result == NULL) result = calc_buffer;
+ temp = alloca(VALUE_SIZE);
+
+ if (_desc(value).exponent_size == exp_size && _desc(value).mantissa_size == mant_size)
+ {
+ if (value != result) memcpy(result, value, CALC_BUFFER_SIZE);
+ return result;
+ }
+
+ /* set the descriptor of the new value */
+ _desc(result).exponent_size = exp_size;
+ _desc(result).mantissa_size = mant_size;
+ _desc(result).class = _desc(value).class;
+
+ _sign(result) = _sign(value);
+
+ /* when the mantissa sizes differ normalizing has to shift to align it.
+ * this would change the exponent, which is unwanted. So calculate this
+ * offset and add it */
+ val_bias = (1<<_desc(value).exponent_size)/2-1;
+ res_bias = (1<<exp_size)/2-1;
+
+ exp_offset = (res_bias - val_bias) - (_desc(value).mantissa_size - mant_size);
+ sc_val_from_long(exp_offset, temp);
+ sc_add(_exp(value), temp, _exp(result));
+
+ /* _normalize expects normalized radix point */
+ if (_desc(val).class == SUBNORMAL) {
+ sc_val_from_ulong(1, NULL);
+ _shift_left(_mant(val), sc_get_buffer(), _mant(result));
+ } else if (value != result) {
+ memcpy(_mant(result), _mant(value), VALUE_SIZE);
+ } else {
+ memmove(_mant(result), _mant(value), VALUE_SIZE);
+ }
+
+ _normalize(result, result, 0);
+ TRACEPRINTF(("Cast results in %s\n", fc_print(result, temp, VALUE_SIZE, FC_PACKED)));
+ return result;
+}
+
+char* fc_get_max(unsigned int exponent_size, unsigned int mantissa_size, char* result)
+{
+ if (result == NULL) result = calc_buffer;
+
+ _desc(result).exponent_size = exponent_size;
+ _desc(result).mantissa_size = mantissa_size;
+ _desc(result).class = NORMAL;
+
+ _sign(result) = 0;
+
+ sc_val_from_ulong((1<<exponent_size) - 2, _exp(result));
+
+ sc_max_from_bits(mantissa_size + 1, 0, _mant(result));
+ sc_val_from_ulong(2, NULL);
+ _shift_left(_mant(result), sc_get_buffer(), _mant(result));
+
+ return result;
+}
+
+char* fc_get_min(unsigned int exponent_size, unsigned int mantissa_size, char *result)
+{
+ if (result == NULL) result = calc_buffer;
+
+ fc_get_max(exponent_size, mantissa_size, result);
+ _sign(result) = 1;
+
+ return result;
+}
+
+char* fc_get_snan(unsigned int exponent_size, unsigned int mantissa_size, char *result)
+{
+ if (result == NULL) result = calc_buffer;
+
+ _desc(result).exponent_size = exponent_size;
+ _desc(result).mantissa_size = mantissa_size;
+ _desc(result).class = NAN;
+
+ _sign(result) = 0;
+
+ sc_val_from_ulong((1<<exponent_size)-1, _exp(result));
+
+ /* signalling nan has non-zero mantissa with msb not set */
+ sc_val_from_ulong(1, _mant(result));
+
+ return result;
+}
+
+char* fc_get_qnan(unsigned int exponent_size, unsigned int mantissa_size, char *result)
+{
+ if (result == NULL) result = calc_buffer;
+
+ _desc(result).exponent_size = exponent_size;
+ _desc(result).mantissa_size = mantissa_size;
+ _desc(result).class = NAN;
+
+ _sign(result) = 0;
+
+ sc_val_from_ulong((1<<exponent_size)-1, _exp(result));
+
+ /* quiet nan has the msb of the mantissa set, so shift one there */
+ sc_val_from_ulong(1, _mant(result));
+ /* mantissa_size >+< 1 because of two extra rounding bits */
+ sc_val_from_ulong(mantissa_size + 1, NULL);
+ _shift_left(_mant(result), sc_get_buffer(), _mant(result));
+
+ return result;
+}
+
+char* fc_get_plusinf(unsigned int exponent_size, unsigned int mantissa_size, char *result)
+{
+ if (result == NULL) result = calc_buffer;
+
+ _desc(result).exponent_size = exponent_size;
+ _desc(result).mantissa_size = mantissa_size;
+ _desc(result).class = NORMAL;
+
+ _sign(result) = 0;
+
+ sc_val_from_ulong((1<<exponent_size)-1, _exp(result));
+
+ sc_val_from_ulong(0, _mant(result));
+
+ return result;
+}
+
+char* fc_get_minusinf(unsigned int exponent_size, unsigned int mantissa_size, char *result)
+{
+ if (result == NULL) result = calc_buffer;
+
+ fc_get_plusinf(exponent_size, mantissa_size, result);
+ _sign(result) = 1;
+
+ return result;
+}
+
+int fc_comp(const void *a, const void *b)
+{
+ const char *val_a = (const char*)a;
+ const char *val_b = (const char*)b;
+
+ /* unordered */
+ if (_desc(val_a).class == NAN || _desc(val_b).class == NAN) return 2;
+ /* zero is equal independent of sign */
+ if ((_desc(val_a).class == ZERO) && (_desc(val_b).class == ZERO)) return 0;
+ /* different signs make compare easy */
+ if (_sign(val_a) != _sign(val_b)) return (_sign(val_a)==0)?(1):(-1);
+ /* both infinity means equality */
+ if ((_desc(val_a).class == INF) && (_desc(val_b).class == INF)) return 0;
+ /* infinity is bigger than the rest */
+ if (_desc(val_a).class == INF) return _sign(val_a)?(-1):(1);
+ if (_desc(val_b).class == INF) return _sign(val_b)?(1):(-1);
+
+ switch (sc_comp(_exp(val_a), _exp(val_b))) {
+ case -1:
+ return -1;
+ case 1:
+ return 1;
+ case 0:
+ return sc_comp(_mant(val_a), _mant(val_b));
+ default:
+ return 2;
+ }
+}
+
+int fc_is_zero(const void *a)
+{
+ return _desc((const char*)a).class == ZERO;
+}
+
+int fc_is_negative(const void *a)
+{
+ return _sign((const char*)a);
+}
+
+int fc_is_inf(const void *a)
+{
+ return _desc(a).class == INF;
+}
+
+int fc_is_nan(const void *a)
+{
+ return _desc(a).class == NAN;
+}
+
+int fc_is_subnormal(const void *a)
+{
+ return _desc(a).class == SUBNORMAL;
+}
+
+char *fc_print(const void *a, char *buf, int buflen, unsigned base)
+{
+ const char *val;
+ char *mul_1;
+
+ val = (const char*)a;
+
+ mul_1 = alloca(CALC_BUFFER_SIZE);
+
+ switch (base) {
+ case FC_DEC:
+ switch (_desc(val).class) {
+ case INF:
+ if (buflen >= 8+_sign(val)) sprintf(buf, "%sINFINITY", _sign(val)?"-":"");
+ else snprintf(buf, buflen, "%sINF", _sign(val)?"-":NULL);
+ break;
+ case NAN:
+ snprintf(buf, buflen, "NAN");
+ break;
+ case ZERO:
+ snprintf(buf, buflen, "0.0");
+ break;
+ default:
+ /* XXX to be implemented */
+#ifdef HAVE_LONG_DOUBLE
+ /* XXX 30 is arbitrary */
+ snprintf(buf, buflen, "%.30LE", fc_val_to_float(val));
+#else
+ snprintf(buf, buflen, "%.18E", fc_val_to_float(val));
+#endif
+ }
+ break;
+
+ case FC_HEX:
+ switch (_desc(val).class) {
+ case INF:
+ if (buflen >= 8+_sign(val)) sprintf(buf, "%sINFINITY", _sign(val)?"-":"");
+ else snprintf(buf, buflen, "%sINF", _sign(val)?"-":NULL);
+ break;
+ case NAN:
+ snprintf(buf, buflen, "NAN");
+ break;
+ case ZERO:
+ snprintf(buf, buflen, "0.0");
+ break;
+ default:
+#ifdef HAVE_LONG_DOUBLE
+ snprintf(buf, buflen, "%LA", fc_val_to_float(val));
+#else
+ snprintf(buf, buflen, "%A", fc_val_to_float(val));
+#endif
+ }
+ break;
+
+ case FC_PACKED:
+ default:
+ snprintf(buf, buflen, "%s", sc_print(_pack(val, mul_1), VALUE_SIZE*4, SC_HEX));
+ break;
+ }