422 lines
12 KiB
C
422 lines
12 KiB
C
// SPDX-FileCopyrightText: 2022 heersin <teablearcher@gmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_util.h>
|
|
|
|
/**
|
|
* \file : Internal function for float
|
|
* \brief : Should be included directly in float.c
|
|
*/
|
|
|
|
static inline ut32 rz_float_info_bin32(RzFloatInfo which_info) {
|
|
switch (which_info) {
|
|
case RZ_FLOAT_INFO_BASE:
|
|
return 2;
|
|
case RZ_FLOAT_INFO_EXP_LEN:
|
|
return 8;
|
|
case RZ_FLOAT_INFO_MAN_LEN:
|
|
return 23;
|
|
case RZ_FLOAT_INFO_TOTAL_LEN:
|
|
return 32;
|
|
case RZ_FLOAT_INFO_BIAS:
|
|
return 127;
|
|
default:
|
|
rz_warn_if_reached();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static inline ut32 rz_float_info_bin64(RzFloatInfo which_info) {
|
|
switch (which_info) {
|
|
case RZ_FLOAT_INFO_BASE:
|
|
return 2;
|
|
case RZ_FLOAT_INFO_EXP_LEN:
|
|
return 11;
|
|
case RZ_FLOAT_INFO_MAN_LEN:
|
|
return 52;
|
|
case RZ_FLOAT_INFO_TOTAL_LEN:
|
|
return 64;
|
|
case RZ_FLOAT_INFO_BIAS:
|
|
return 1023;
|
|
default:
|
|
rz_warn_if_reached();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static inline ut32 rz_float_info_bin128(RzFloatInfo which_info) {
|
|
switch (which_info) {
|
|
case RZ_FLOAT_INFO_BASE:
|
|
return 2;
|
|
case RZ_FLOAT_INFO_EXP_LEN:
|
|
return 15;
|
|
case RZ_FLOAT_INFO_MAN_LEN:
|
|
return 112;
|
|
case RZ_FLOAT_INFO_TOTAL_LEN:
|
|
return 128;
|
|
case RZ_FLOAT_INFO_BIAS:
|
|
return 16383;
|
|
default:
|
|
rz_warn_if_reached();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shift right, but keeps LSB true if hit 1 during shift
|
|
* \param x RzBitVector, pointer to bv
|
|
* \param dist shift distance, positive or zero
|
|
* \return ret bool, return true if shift success
|
|
*/
|
|
static bool rz_bv_shift_right_jammed(RzBitVector *bv, ut32 dist) {
|
|
rz_return_val_if_fail(bv, false);
|
|
|
|
bool lsb = false;
|
|
for (ut32 i = 0; i < dist; ++i) {
|
|
bool b = rz_bv_get(bv, i);
|
|
if (b) {
|
|
lsb = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rz_bv_rshift(bv, dist);
|
|
rz_bv_set(bv, 0, lsb);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get a bitvector representation of exponent, have the same length of parameter `bv`
|
|
* \param bv RzBitVector, the bitvector interpreted as float
|
|
* \param format RzFloatFormat, specifying the format of float
|
|
* \return a bitvector representation of exponent
|
|
*/
|
|
static RZ_OWN RzBitVector *get_exp(RZ_NONNULL RzBitVector *bv, RzFloatFormat format) {
|
|
rz_return_val_if_fail(bv, NULL);
|
|
|
|
ut32 exp_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_EXP_LEN);
|
|
ut32 man_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_MAN_LEN);
|
|
RzBitVector *res = rz_bv_new(exp_len + man_len + 1);
|
|
if (!res) {
|
|
RZ_LOG_ERROR("rz_float : failed to create bitvector");
|
|
return NULL;
|
|
}
|
|
rz_bv_copy_nbits(bv, man_len, res, 0, exp_len);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Get a bitvector representation of mantissa, have the same length of parameter `bv`
|
|
* \param bv RzBitVector, the bitvector interpreted as float
|
|
* \param format RzFloatFormat, specifying the format of float
|
|
* \return a bitvector representation of mantissa
|
|
*/
|
|
static RZ_OWN RzBitVector *get_man(RZ_NONNULL RzBitVector *bv, RzFloatFormat format) {
|
|
rz_return_val_if_fail(bv, NULL);
|
|
|
|
ut32 exp_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_EXP_LEN);
|
|
ut32 man_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_MAN_LEN);
|
|
RzBitVector *res = rz_bv_new(exp_len + man_len + 1);
|
|
if (!res) {
|
|
RZ_LOG_ERROR("rz_float : failed to create bitvector");
|
|
return NULL;
|
|
}
|
|
rz_bv_copy_nbits(bv, 0, res, 0, man_len);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Get a bitvector representation of mantissa, twice as long as `bv` length.
|
|
* \param bv RzBitVector, the bitvector interpreted as float
|
|
* \param format RzFloatFormat, specifying the format of float
|
|
* \return a bitvector representation of mantissa
|
|
*/
|
|
static RZ_OWN RzBitVector *get_man_stretched(RZ_NONNULL RzBitVector *bv, RzFloatFormat format) {
|
|
rz_return_val_if_fail(bv, NULL);
|
|
|
|
ut32 man_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_MAN_LEN);
|
|
ut32 total = rz_float_get_format_info(format, RZ_FLOAT_INFO_TOTAL_LEN);
|
|
RzBitVector *res = rz_bv_new(total * 2);
|
|
if (!res) {
|
|
RZ_LOG_ERROR("rz_float : failed to create bitvector");
|
|
return NULL;
|
|
}
|
|
rz_bv_copy_nbits(bv, 0, res, 0, man_len);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Get a bitvector representation of exponent. The length is depending on the exponent width (specified by `format`)
|
|
* \param bv RzBitVector, the bitvector interpreted as float
|
|
* \param format RzFloatFormat, specifying the format of float
|
|
* \return a bitvector representation of exponent
|
|
*/
|
|
static RZ_OWN RzBitVector *get_exp_squashed(RZ_NONNULL RzBitVector *bv, RzFloatFormat format) {
|
|
rz_return_val_if_fail(bv, NULL);
|
|
|
|
ut32 exp_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_EXP_LEN);
|
|
ut32 man_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_MAN_LEN);
|
|
RzBitVector *res = rz_bv_new(exp_len);
|
|
if (!res) {
|
|
RZ_LOG_ERROR("rz_float : failed to create bitvector");
|
|
return NULL;
|
|
}
|
|
rz_bv_copy_nbits(bv, man_len, res, 0, exp_len);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Get a bitvector representation of mantissa. The length is depending on the mantissa width (specified by `format`)
|
|
* \param bv RzBitVector, the bitvector interpreted as float
|
|
* \param format RzFloatFormat, specifying the format of float
|
|
* \return a bitvector representation of mantissa
|
|
*/
|
|
static RZ_OWN RzBitVector *get_man_squashed(RZ_NONNULL RzBitVector *bv, RzFloatFormat format) {
|
|
rz_return_val_if_fail(bv, NULL);
|
|
|
|
ut32 man_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_MAN_LEN);
|
|
RzBitVector *res = rz_bv_new(man_len);
|
|
if (!res) {
|
|
RZ_LOG_ERROR("rz_float : failed to create bitvector");
|
|
return NULL;
|
|
}
|
|
rz_bv_copy_nbits(bv, 0, res, 0, man_len);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Get the sign of bv
|
|
* \param bv RzBitVector, the bitvector interpreted as float
|
|
* \param format RzFloatFormat, specifying the format of float
|
|
* \return bool sign of float bv
|
|
*/
|
|
static RZ_OWN bool get_sign(RZ_NONNULL RzBitVector *bv, RzFloatFormat format) {
|
|
rz_return_val_if_fail(bv, NULL);
|
|
|
|
return rz_bv_get(bv, bv->len - 1);
|
|
}
|
|
|
|
/**
|
|
* make a float becomes positive, would changed the float itself
|
|
* \param f float to be converted
|
|
* \return true if success
|
|
*/
|
|
static bool rz_make_fabs(RzFloat *f) {
|
|
return rz_bv_set(f->s, f->s->len - 1, false);
|
|
}
|
|
|
|
/**
|
|
* get the half value of a float (by decreasing exponent value)
|
|
* \param f float
|
|
* \return half value of a float
|
|
*/
|
|
static RzFloat *rz_half_float(RzFloat *f) {
|
|
ut32 total = rz_float_get_format_info(f->r, RZ_FLOAT_INFO_TOTAL_LEN);
|
|
ut32 exp_start = rz_float_get_format_info(f->r, RZ_FLOAT_INFO_MAN_LEN);
|
|
|
|
// for exp sub 1
|
|
RzBitVector *sub = rz_bv_new(total);
|
|
rz_bv_set(sub, exp_start, true);
|
|
RzFloat *half = rz_float_new(f->r);
|
|
half->s = rz_bv_sub(f->s, sub, NULL);
|
|
|
|
rz_bv_free(sub);
|
|
return half;
|
|
}
|
|
|
|
/**
|
|
* Pack sign, exponent, and significant together to float bv
|
|
* \param sign sign of float
|
|
* \param exp exponent part, can be squashed or normal
|
|
* \param sig significant part (mantissa with a leading bit 1), can be squashed or normal
|
|
* \param format format of float
|
|
* \return RzBitVector the final bitvector representation of RzFloat
|
|
*/
|
|
static RZ_OWN RzBitVector *pack_float_bv(bool sign, RzBitVector *exp, RzBitVector *sig, RzFloatFormat format) {
|
|
ut32 exp_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_EXP_LEN);
|
|
ut32 man_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_MAN_LEN);
|
|
ut32 total = man_len + exp_len + 1;
|
|
RzBitVector *ret = rz_bv_new(total);
|
|
// copy exp to ret
|
|
rz_bv_copy_nbits(exp, 0, ret, man_len, exp_len);
|
|
rz_bv_copy_nbits(sig, 0, ret, 0, man_len);
|
|
rz_bv_set(ret, total - 1, sign);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Detecting if a significant should be rounded
|
|
* \param sig RzBitVector significant bv before rounding `point` is at the 2nd bit counted from MSB (01.MM MMMM ...)
|
|
* \param r_bits_bound ut32 boundary of round bits
|
|
* \return bool return true if significant should be rounded, else return false
|
|
*/
|
|
static inline bool detect_should_round(RzBitVector *sig, ut32 r_bits_bound) {
|
|
bool should_round = false;
|
|
for (ut32 i = 0; i < r_bits_bound; ++i) {
|
|
if (rz_bv_get(sig, i) == true) {
|
|
should_round = true;
|
|
break;
|
|
}
|
|
}
|
|
return should_round;
|
|
}
|
|
|
|
/**
|
|
* Detecting if the round bits is in the halfway (MSB is 1, the other bits is 0)
|
|
* \param sig RzBitVector significant bv before rounding `point` is at the 2nd bit counted from MSB (01.MM MMMM ...)
|
|
* \param r_bits_bound ut32 boundary of round bits
|
|
* \return bool return true if significant should be rounded, else return false
|
|
*/
|
|
static bool detect_halfway(RzBitVector *sig, ut32 r_bits_bound) {
|
|
for (ut32 i = 0; i < r_bits_bound - 1; ++i) {
|
|
if (rz_bv_get(sig, i) == true) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (rz_bv_get(sig, r_bits_bound - 1) == true) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Generate an infinite bitvector
|
|
* \param sign sign of an inf
|
|
* \param format RzFloatFormat format of float
|
|
* \return an infinite bitvector
|
|
*/
|
|
static RZ_OWN RzBitVector *gen_inf_bv(bool sign, RzFloatFormat format) {
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Trying to round float component
|
|
* \param sign sign of float
|
|
* \param exp ut32 value of exponent
|
|
* \param sig RzBitVector significant bv before rounding `point` is at the 2nd bit counted from MSB (01.MM MMMM ...)
|
|
* \param format RzFloatFormat format of float
|
|
* \param mode Rounding mode
|
|
* \return RzFloat A rounded float
|
|
*/
|
|
static RZ_OWN RzFloat *
|
|
round_float_bv(bool sign, ut32 exp, RzBitVector *sig, RzFloatFormat format, RzFloatRMode mode) {
|
|
ut32 bias = rz_float_get_format_info(format, RZ_FLOAT_INFO_BIAS);
|
|
ut32 emax = ((bias + 1) << 1) - 1;
|
|
ut32 exp_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_EXP_LEN);
|
|
ut32 total_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_TOTAL_LEN);
|
|
|
|
bool is_rne = (mode == RZ_FLOAT_RMODE_RNE);
|
|
bool is_rna = (mode == RZ_FLOAT_RMODE_RNA);
|
|
RzFloat *ret = RZ_NEW0(RzFloat);
|
|
ret->r = format;
|
|
ret->s = NULL;
|
|
|
|
// add 1 to the LSB of sig
|
|
ut32 round_inc_val = (bias + 1) >> 1;
|
|
|
|
// handle round to max(+inf)/min(-inf)
|
|
// if + && round towards +inf : use bias as inc
|
|
// if - && round towards -inf : use bias as inc
|
|
if (!is_rne && !is_rna) {
|
|
round_inc_val =
|
|
(mode == (sign ? RZ_FLOAT_RMODE_RTN : RZ_FLOAT_RMODE_RTP))
|
|
? bias
|
|
: 0;
|
|
}
|
|
|
|
// get round bits
|
|
// every num before rounding have the following pattern
|
|
// 01MM MMMM MMMM ...
|
|
// we will leave (sign_len + exp_len) bits before mantissa part
|
|
// and thus the lower (sign_len + exp_len - 2) bits will be r-shifted out
|
|
// in another word, the lower bits will be guard bit, round bit and sticky bits
|
|
ut32 round_bits_bound = (exp_len + 1 - 2);
|
|
ut32 should_round = detect_should_round(sig, round_bits_bound);
|
|
// ut32 guard_bit_pos = round_bits_bound - 1;
|
|
ut32 is_halfway = detect_halfway(sig, round_bits_bound);
|
|
|
|
RzBitVector *possible_sig = NULL;
|
|
bool unused;
|
|
RzBitVector *round_inc_bv = rz_bv_new_from_ut64(sig->len, round_inc_val);
|
|
possible_sig = rz_bv_add(sig, round_inc_bv, &unused);
|
|
|
|
if (exp >= emax - 2) {
|
|
// handle overflow and underflow
|
|
if ((st32)exp < 0) {
|
|
// extremely small
|
|
bool is_tiny = (exp < -1) || (!(rz_bv_msb(possible_sig)));
|
|
|
|
rz_bv_shift_right_jammed(possible_sig, (ut32)(-(st32)exp));
|
|
exp = 0;
|
|
|
|
// update round info
|
|
should_round = detect_should_round(possible_sig, round_bits_bound);
|
|
is_halfway = detect_halfway(sig, round_bits_bound);
|
|
|
|
if (is_tiny && should_round) {
|
|
ret->exception |= RZ_FLOAT_E_UNDERFLOW;
|
|
}
|
|
} else if ((exp > emax - 2) || (rz_bv_msb(possible_sig))) {
|
|
// overflow
|
|
ret->exception |= RZ_FLOAT_E_OVERFLOW;
|
|
ret->exception |= RZ_FLOAT_E_INEXACT;
|
|
|
|
// gen a num near inf
|
|
if (round_inc_val) {
|
|
ret->s = gen_inf_bv(sign, format);
|
|
} else {
|
|
RzBitVector *one = rz_bv_new_one(total_len);
|
|
RzBitVector *inf = gen_inf_bv(sign, format);
|
|
ret->s = rz_bv_sub(inf, one, &unused);
|
|
rz_bv_free(one);
|
|
rz_bv_free(inf);
|
|
inf = NULL;
|
|
one = NULL;
|
|
}
|
|
|
|
rz_bv_free(possible_sig);
|
|
rz_bv_free(round_inc_bv);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
// shift for packing
|
|
rz_bv_rshift(possible_sig, round_bits_bound);
|
|
|
|
if (should_round) {
|
|
ret->exception |= RZ_FLOAT_E_INEXACT;
|
|
}
|
|
|
|
// detect half way
|
|
if (is_halfway && is_rne) {
|
|
// set lsb == 0
|
|
rz_bv_set(possible_sig, 0, false);
|
|
}
|
|
|
|
if (rz_bv_is_zero_vector(possible_sig)) {
|
|
// NaN
|
|
exp = 0;
|
|
}
|
|
|
|
// pack float
|
|
RzBitVector *exp_bv = rz_bv_new_from_ut64(total_len, exp);
|
|
ut32 man_len = rz_float_get_format_info(format, RZ_FLOAT_INFO_MAN_LEN);
|
|
rz_bv_lshift(exp_bv, man_len);
|
|
ret->s = rz_bv_add(exp_bv, possible_sig, &unused);
|
|
rz_bv_set(ret->s, total_len - 1, sign);
|
|
|
|
// clean
|
|
rz_bv_free(round_inc_bv);
|
|
rz_bv_free(exp_bv);
|
|
rz_bv_free(possible_sig);
|
|
|
|
return ret;
|
|
}
|