Use SoftFloat 3e for implementing arithmetic operations in RzFloat (#4535)

* Add a new test for checking 80-bit floating point operations

    * New test `f80_ieee_div_test` tests the division of two 80-bit
      floats

* Add SoftFloat 2c as a meson subproject

* Add softfloat code to make the failing test case pass

* Update the hash for the latest softfloat revision

* Implement `rz_float_sqrt` using SoftFloat

* Run the `f80_ieee_div_test` only for x86

* Replace SoftFloat version 2c with 3e

    * 3e has less bugs and more features
    * Modify the implementation in accordance

* Update SoftFloat revision and add a guard around the 80-bit div test

* Use SoftFloat for add, sub, mul operations as well

* Make rem and mod also use SoftFloat functions

    * Also add test for mod and rem, and fix behavior of rem

* Add comment about behavior of mod and rem

* Add comments for tests which have different results for mod and rem

* Simplify usage of loop variable as suggested in review

* Remove unused macro from float.c

* Implement `FMA` and `ROUND` using SoftFloat API

* Add more tests for 80-bit floats

* Change remote to a repository under rizinorg

* Add info about the rounding mode in the Doxygen for rem and mod

* Add comments in tests for rem and mod in `test_float.c`

* Use bitvectors to initialize 80-bit soft floats

    * This makes the tests more portable and hence they can be run on
      any platform

* Remove guards for f80 tests since they are portable now
This commit is contained in:
Dhruv Maroo 2024-06-25 22:06:24 +05:30 committed by GitHub
parent 3a33626a33
commit 19ec6ba298
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 489 additions and 1302 deletions

1
.gitignore vendored
View file

@ -130,6 +130,7 @@ subprojects/libmspack/
subprojects/blake3/
subprojects/xz-*/
subprojects/zstd-*/
subprojects/softfloat/
dist/windows/Output
# Core files generated by OpenBSD
*.core

View file

@ -81,6 +81,7 @@ At the time of writing, these are:
* `use_sys_libmspack`
* `use_sys_pcre2`
* `use_sys_tree_sitter`
* `use_sys_softfloat`
See [meson_options.txt][] for a complete list of compile-time options.

File diff suppressed because it is too large Load diff

View file

@ -246,26 +246,6 @@ 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);
rz_bv_free(half->s);
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

View file

@ -93,7 +93,7 @@ rz_util_common_sources = [
]
rz_util_sources = rz_util_common_sources
rz_util_deps = [ldl, lrt, mth, th, utl, pcre2_dep] + platform_deps
rz_util_deps = [ldl, lrt, mth, th, utl, pcre2_dep, softfloat_dep] + platform_deps
if zlib_dep.found()
rz_util_deps += [zlib_dep]
endif

View file

@ -608,6 +608,18 @@ if get_option('use_lzma')
endif
endif
# handle softfloat dependency
r = run_command(py3_exe, check_meson_subproject_py, 'softfloat', check: false)
if r.returncode() == 1 and get_option('subprojects_check')
error(subproject_clean_error_msg)
endif
softfloat_dep = dependency('softfloat', required: get_option('use_sys_softfloat'), static: is_static_build)
if not softfloat_dep.found()
softfloat_proj = subproject('softfloat', default_options: ['default_library=static', 'warning_level=0'])
softfloat_dep = softfloat_proj.get_variable('softfloat_dep')
endif
# handle zip dependency
r = run_command(py3_exe, check_meson_subproject_py, 'libzip', check: false)
if r.returncode() == 1 and get_option('subprojects_check')
@ -798,6 +810,7 @@ summary({
'System tree-sitter library': tree_sitter_dep.found() and tree_sitter_dep.type_name() != 'internal',
'System lz4 library': lz4_dep.found() and lz4_dep.type_name() != 'internal',
'System lzma library': liblzma_dep.found() and liblzma_dep.type_name() != 'internal',
'System softfloat library': softfloat_dep.found() and softfloat_dep.type_name() != 'internal',
'System zlib library': zlib_dep.found() and zlib_dep.type_name() != 'internal',
'System zstd library': libzstd_dep.found() and libzstd_dep.type_name() != 'internal',
'System zip library': libzip_dep.found() and libzip_dep.type_name() != 'internal',

View file

@ -36,6 +36,7 @@ option('use_sys_openssl', type: 'feature', value: 'disabled')
option('use_sys_libmspack', type: 'feature', value: 'disabled')
option('use_sys_tree_sitter', type: 'feature', value: 'disabled')
option('use_sys_pcre2', type: 'feature', value: 'disabled')
option('use_sys_softfloat', type: 'feature', value: 'disabled')
option('use_swift_demangler', type: 'boolean', value: true, description: 'If false, disables the swift demangler')
option('use_gpl', type: 'boolean', value: true, description: 'Set to false when you want to disable gpl code')
option('install_sigdb', type: 'boolean', value: false, description: 'Downloads and installs rizin sigdb')

View file

@ -0,0 +1,5 @@
[wrap-git]
url = https://github.com/rizinorg/softfloat
revision = e06f4fcbdc6b3545c0624ac70725daf95eda53e8
directory = softfloat
depth = 1

View file

@ -558,9 +558,15 @@ bool f32_ieee_special_num_test(void) {
}
bool f32_ieee_rem_test(void) {
/* mod(x, y) = x - round(x/y, RNE) * y */
/* This test should return a different result in mod. */
RzFloat *a1 = rz_float_new_from_f32(4.0f);
RzFloat *b1 = rz_float_new_from_f32(1.5f);
RzFloat *expect1 = rz_float_new_from_f32(1.0f);
RzFloat *expect1 = rz_float_new_from_f32(-0.5f);
/* quot = x/y = 4.0/1.5 = 2.666...
* rounded_quot (RNE) = 3
*/
RzFloat *rem1 = rz_float_rem(a1, b1, RZ_FLOAT_RMODE_RNE);
mu_assert_true(is_equal_float(rem1, expect1), "rem test 1");
rz_float_free(a1);
@ -578,9 +584,13 @@ bool f32_ieee_rem_test(void) {
rz_float_free(expect2);
rz_float_free(rem2);
RzFloat *a3 = rz_float_new_from_ut32_as_f32(0x3F7FFF3F);
RzFloat *b3 = rz_float_new_from_ut32_as_f32(0x957CE0B6);
RzFloat *expect3 = rz_float_new_from_ut32_as_f32(0x145F53B0);
/* This test should return a different result in mod. */
RzFloat *a3 = rz_float_new_from_ut32_as_f32(0xCBF83FFF);
RzFloat *b3 = rz_float_new_from_ut32_as_f32(0x44800FF0);
RzFloat *expect3 = rz_float_new_from_ut32_as_f32(0x43E63BC0);
/* quot = x/y = -32538622.0/1024.498046875 = -31760.550543997346
* rounded_quot (RNE) = -31761
*/
RzFloat *rem3 = rz_float_rem(a3, b3, RZ_FLOAT_RMODE_RNE);
mu_assert_true(is_equal_float(rem3, expect3), "rem test 3");
rz_float_free(a3);
@ -588,6 +598,70 @@ bool f32_ieee_rem_test(void) {
rz_float_free(expect3);
rz_float_free(rem3);
RzFloat *a4 = rz_float_new_from_ut32_as_f32(0x3F7FFF3F);
RzFloat *b4 = rz_float_new_from_ut32_as_f32(0x957CE0B6);
RzFloat *expect4 = rz_float_new_from_ut32_as_f32(0x145F53B0);
RzFloat *rem4 = rz_float_rem(a4, b4, RZ_FLOAT_RMODE_RNE);
mu_assert_true(is_equal_float(rem4, expect4), "rem test 4");
rz_float_free(a4);
rz_float_free(b4);
rz_float_free(expect4);
rz_float_free(rem4);
mu_end;
}
bool f32_ieee_mod_test(void) {
/* mod(x, y) = x - round(x/y, RTZ) * y */
/* This test should return a different result in rem. */
RzFloat *a1 = rz_float_new_from_f32(4.0f);
RzFloat *b1 = rz_float_new_from_f32(1.5f);
RzFloat *expect1 = rz_float_new_from_f32(1.0f);
/* quot = x/y = 4.0/1.5 = 2.666...
* rounded_quot (RTZ) = 2
*/
RzFloat *rem1 = rz_float_mod(a1, b1, RZ_FLOAT_RMODE_RNE);
mu_assert_true(is_equal_float(rem1, expect1), "rem test 1");
rz_float_free(a1);
rz_float_free(b1);
rz_float_free(expect1);
rz_float_free(rem1);
RzFloat *a2 = rz_float_new_from_ut32_as_f32(0xCBF83FFF);
RzFloat *b2 = rz_float_new_from_ut32_as_f32(0x44801003);
RzFloat *expect2 = rz_float_new_from_ut32_as_f32(0xC3F52F40);
RzFloat *rem2 = rz_float_mod(a2, b2, RZ_FLOAT_RMODE_RNE);
mu_assert_true(is_equal_float(rem2, expect2), "rem test 2");
rz_float_free(a2);
rz_float_free(b2);
rz_float_free(expect2);
rz_float_free(rem2);
/* This test should return a different result in rem. */
RzFloat *a3 = rz_float_new_from_ut32_as_f32(0xCBF83FFF);
RzFloat *b3 = rz_float_new_from_ut32_as_f32(0x44801002);
RzFloat *expect3 = rz_float_new_from_ut32_as_f32(0xC3F71F80);
/* quot = x/y = -32538622.0/1024.498046875 = -31760.550543997346
* rounded_quot (RTZ) = -31760
*/
RzFloat *rem3 = rz_float_mod(a3, b3, RZ_FLOAT_RMODE_RNE);
mu_assert_true(is_equal_float(rem3, expect3), "rem test 3");
rz_float_free(a3);
rz_float_free(b3);
rz_float_free(expect3);
rz_float_free(rem3);
RzFloat *a4 = rz_float_new_from_ut32_as_f32(0x3F7FFF3F);
RzFloat *b4 = rz_float_new_from_ut32_as_f32(0x957CE0B6);
RzFloat *expect4 = rz_float_new_from_ut32_as_f32(0x145F53B0);
RzFloat *rem4 = rz_float_mod(a4, b4, RZ_FLOAT_RMODE_RNE);
mu_assert_true(is_equal_float(rem4, expect4), "rem test 4");
rz_float_free(a4);
rz_float_free(b4);
rz_float_free(expect4);
rz_float_free(rem4);
mu_end;
}
@ -1414,10 +1488,91 @@ bool f32_ieee_cast_test(void) {
mu_end;
}
bool f80_round_test(void) {
static RzFloat *new_f80_from_bytes(const char *bytes) {
RzBitVector *bv = rz_bv_new_from_bytes_be((const unsigned char *)bytes, 0, 80);
RzFloat *ret = rz_float_new_from_bv(bv);
rz_bv_free(bv);
return ret;
}
bool f80_ieee_add_test(void) {
RzFloat *x_f80 = new_f80_from_bytes("\x40\x00\xa6\x5f\x8f\x48\x12\x44\xca\x0b");
RzFloat *y_f80 = new_f80_from_bytes("\x3f\xfd\xc4\xf4\x67\x6e\x7d\x93\x40\x00");
RzFloat *sum_f80 = rz_float_add(x_f80, y_f80, RZ_FLOAT_RMODE_RNE);
RzFloat *expected_f80 = new_f80_from_bytes("\x40\x00\xbe\xfe\x1c\x35\xe1\xf7\x32\x0b");
mu_assert_false(rz_float_cmp(sum_f80, expected_f80), "Add 80-bit floats");
rz_float_free(x_f80);
rz_float_free(y_f80);
rz_float_free(sum_f80);
rz_float_free(expected_f80);
mu_end;
}
bool f80_ieee_sub_test(void) {
RzFloat *x_f80 = new_f80_from_bytes("\x40\x00\xa6\x5f\x8f\x48\x12\x44\xca\x0b");
RzFloat *y_f80 = new_f80_from_bytes("\x3f\xfd\xc4\xf4\x67\x6e\x7d\x93\x40\x00");
RzFloat *diff_f80 = rz_float_sub(x_f80, y_f80, RZ_FLOAT_RMODE_RNE);
RzFloat *expected_f80 = new_f80_from_bytes("\x40\x00\x8d\xc1\x02\x5a\x42\x92\x62\x0b");
mu_assert_false(rz_float_cmp(diff_f80, expected_f80), "Subtract 80-bit floats");
rz_float_free(x_f80);
rz_float_free(y_f80);
rz_float_free(diff_f80);
rz_float_free(expected_f80);
mu_end;
}
bool f80_ieee_mul_test(void) {
RzFloat *x_f80 = new_f80_from_bytes("\x40\x00\xa6\x5f\x8f\x48\x12\x44\xca\x0b");
RzFloat *y_f80 = new_f80_from_bytes("\x3f\xfd\xc4\xf4\x67\x6e\x7d\x93\x40\x00");
RzFloat *prod_f80 = rz_float_mul(x_f80, y_f80, RZ_FLOAT_RMODE_RNE);
RzFloat *expected_f80 = new_f80_from_bytes("\x3f\xff\x80\x00\x00\x00\x00\x00\x00\x00");
mu_assert_false(rz_float_cmp(prod_f80, expected_f80), "Multiply 80-bit floats");
rz_float_free(x_f80);
rz_float_free(y_f80);
rz_float_free(prod_f80);
rz_float_free(expected_f80);
mu_end;
}
bool f80_ieee_div_test(void) {
RzFloat *x_f80 = new_f80_from_bytes("\x3f\xff\x80\x00\x00\x00\x00\x00\x00\x00");
RzFloat *y_f80 = new_f80_from_bytes("\xbf\xfd\xc4\xf4\x67\x6e\x7d\x93\x40\x00");
RzFloat *quot_f80 = rz_float_div(x_f80, y_f80, RZ_FLOAT_RMODE_RNE);
RzFloat *expected_f80 = new_f80_from_bytes("\xc0\x00\xa6\x5f\x8f\x48\x12\x44\xca\x0b");
mu_assert_false(rz_float_cmp(quot_f80, expected_f80), "Divide 80-bit floats");
rz_float_free(x_f80);
rz_float_free(y_f80);
rz_float_free(quot_f80);
rz_float_free(expected_f80);
mu_end;
}
bool f80_ieee_sqrt_test(void) {
RzFloat *x_f80 = new_f80_from_bytes("\x3f\xff\xab\x27\x32\x90\xa7\x8b\x0c\x29");
RzFloat *sqrt_f80 = rz_float_sqrt(x_f80, RZ_FLOAT_RMODE_RNE);
RzFloat *expected_f80 = new_f80_from_bytes("\x3f\xff\x94\x03\x1c\xc0\x8d\xdc\xfb\xb5");
mu_assert_false(rz_float_cmp(sqrt_f80, expected_f80), "Square root 80-bit floats");
rz_float_free(x_f80);
rz_float_free(sqrt_f80);
rz_float_free(expected_f80);
mu_end;
}
bool f80_ieee_cast_test(void) {
/* To 80-bit */
RzFloat *old_f = rz_float_new_from_f64(14.285714285714286);
RzFloat *expect_f = rz_float_new_from_f80(14.2857142857142864756l);
RzFloat *expect_f = new_f80_from_bytes("\x40\x02\xe4\x92\x49\x24\x92\x49\x28\x00");
RzFloat *new_cast = rz_float_convert(old_f, RZ_FLOAT_IEEE754_BIN_80, RZ_FLOAT_RMODE_RNE);
mu_assert_false(rz_float_cmp(expect_f, new_cast), "test convert 14.285714285714286d to 14.2857142857142864756l");
rz_float_free(old_f);
@ -1425,23 +1580,21 @@ bool f80_round_test(void) {
rz_float_free(new_cast);
/* From 80-bit */
old_f = rz_float_new_from_f80(13.37l);
old_f = new_f80_from_bytes("\x40\x02\xd5\xeb\x85\x1e\xb8\x51\xeb\x85");
expect_f = rz_float_new_from_f32(13.37f);
new_cast = rz_float_convert(old_f, RZ_FLOAT_IEEE754_BIN_32, RZ_FLOAT_RMODE_RNE);
mu_assert_false(rz_float_cmp(expect_f, new_cast), "test convert 13.37l to 13.37f");
rz_float_free(old_f);
rz_float_free(expect_f);
rz_float_free(new_cast);
mu_end;
/* From 80-bit to 80-bit (should lead to the same value) */
old_f = rz_float_new_from_f80(66668466788774.6870804l);
expect_f = rz_float_new_from_f80(66668466788774.6870804l);
old_f = new_f80_from_bytes("\x40\x2c\xf2\x89\xd9\x1f\x66\x9a\xbf\x92");
new_cast = rz_float_convert(old_f, RZ_FLOAT_IEEE754_BIN_80, RZ_FLOAT_RMODE_RNE);
mu_assert_false(rz_float_cmp(expect_f, new_cast), "test convert 66668466788774.6870804l to itself");
mu_assert_false(rz_float_cmp(old_f, new_cast), "test convert 66668466788774.6870804l to itself");
rz_float_free(old_f);
rz_float_free(expect_f);
rz_float_free(new_cast);
mu_end;
}
@ -1459,6 +1612,7 @@ bool all_tests() {
mu_run_test(f32_ieee_round_test);
mu_run_test(f32_ieee_sqrt_test);
mu_run_test(f32_ieee_rem_test);
mu_run_test(f32_ieee_mod_test);
mu_run_test(f32_ieee_special_num_test);
mu_run_test(float_load_from_bitvector);
mu_run_test(float_print_num);
@ -1468,7 +1622,12 @@ bool all_tests() {
mu_run_test(f32_new_round_test);
mu_run_test(f32_ieee_fround_test);
mu_run_test(f32_ieee_cast_test);
mu_run_test(f80_round_test);
mu_run_test(f80_ieee_add_test);
mu_run_test(f80_ieee_sub_test);
mu_run_test(f80_ieee_mul_test);
mu_run_test(f80_ieee_div_test);
mu_run_test(f80_ieee_sqrt_test);
mu_run_test(f80_ieee_cast_test);
return tests_passed != tests_run;
}