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:
parent
3a33626a33
commit
19ec6ba298
9 changed files with 489 additions and 1302 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -130,6 +130,7 @@ subprojects/libmspack/
|
|||
subprojects/blake3/
|
||||
subprojects/xz-*/
|
||||
subprojects/zstd-*/
|
||||
subprojects/softfloat/
|
||||
dist/windows/Output
|
||||
# Core files generated by OpenBSD
|
||||
*.core
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
13
meson.build
13
meson.build
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
5
subprojects/softfloat.wrap
Normal file
5
subprojects/softfloat.wrap
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[wrap-git]
|
||||
url = https://github.com/rizinorg/softfloat
|
||||
revision = e06f4fcbdc6b3545c0624ac70725daf95eda53e8
|
||||
directory = softfloat
|
||||
depth = 1
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue