From ec1fb35ad4310ff57f69d8982d6dc8cfb8c78a99 Mon Sep 17 00:00:00 2001 From: Richard Holland Date: Mon, 7 Nov 2022 13:53:32 +0000 Subject: [PATCH] more float testing and bug fixes --- src/ripple/app/hook/impl/applyHook.cpp | 75 +++-- src/test/app/SetHook_test.cpp | 395 +++++++++++++++++++++++-- 2 files changed, 421 insertions(+), 49 deletions(-) diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index 61c0fba9a..65eed42ff 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -464,6 +464,30 @@ namespace hook_float return 0; } + // even after adjustment the mantissa can be outside the range by one place + // improving the math above would probably alleviate the need for these branches + if (man < minMantissa) + { + if (man == minMantissa - 1LL) + man += 1LL; + else + { + man *= 10LL; + exp--; + } + } + + if (man > maxMantissa) + { + if (man == maxMantissa + 1LL) + man -= 1LL; + else + { + man /= 10LL; + exp++; + } + } + if (exp < minExponent) { man = 0; @@ -471,6 +495,12 @@ namespace hook_float return 0; } + if (man == 0) + { + exp = 0; + return 0; + } + if (exp > maxExponent) return XFL_OVERFLOW; @@ -4634,7 +4664,12 @@ DEFINE_HOOK_FUNCTION( is_negative ); } -inline int64_t float_divide_internal(int64_t float1, int64_t float2) + +const int64_t float_one_internal = make_float(1000000000000000ull, -15, false); + +inline +int64_t +float_divide_internal(int64_t float1, int64_t float2) { RETURN_IF_INVALID_FLOAT(float1); RETURN_IF_INVALID_FLOAT(float2); @@ -4643,6 +4678,11 @@ inline int64_t float_divide_internal(int64_t float1, int64_t float2) if (float1 == 0) return 0; + // special case: division by 1 + // RH TODO: add more special cases (division by power of 10) + if (float2 == float_one_internal) + return float1; + uint64_t man1 = get_mantissa(float1); int32_t exp1 = get_exponent(float1); bool neg1 = is_negative(float1); @@ -4650,15 +4690,9 @@ inline int64_t float_divide_internal(int64_t float1, int64_t float2) int32_t exp2 = get_exponent(float2); bool neg2 = is_negative(float2); - printf("man1: %ld\n", man1); - printf("man2: %ld\n", man2); - int64_t tmp1 = normalize_xfl(man1, exp1); int64_t tmp2 = normalize_xfl(man2, exp2); - printf("tmp1: %ld\n", tmp1); - printf("tmp2: %ld\n", tmp2); - if (tmp1 < 0 || tmp2 < 0) return INVALID_FLOAT; @@ -4684,6 +4718,7 @@ inline int64_t float_divide_internal(int64_t float1, int64_t float2) uint64_t man3 = 0; int32_t exp3 = exp1 - exp2; + while (man2 > 0) { int i = 0; @@ -4697,9 +4732,6 @@ inline int64_t float_divide_internal(int64_t float1, int64_t float2) exp3--; } - printf("man3: %ld\n", man3); - printf("exp3: %d\n", exp3); - //RHUPTO: debug this bool neg3 = !((neg1 && neg2) || (!neg1 && !neg2)); return normalize_xfl(man3, exp3, neg3); @@ -4712,7 +4744,6 @@ DEFINE_HOOK_FUNCTION( { return float_divide_internal(float1, float2); } -const int64_t float_one_internal = make_float(1000000000000000ull, -15, false); DEFINE_HOOK_FUNCTION( @@ -4818,21 +4849,29 @@ inline int64_t double_to_xfl(double x) // next adjust it into the valid mantissa range (this means dividing by its order and multiplying by 10**15) absresult *= pow(10, -exp_out + 15); - // now a tricky step: sometimes due to IEEE rounding the value may still fall below the minMantissa + // after adjustment the value may still fall below the minMantissa int64_t result = (int64_t)absresult; if (result < minMantissa) { - // if it does then pull it up - result *= 10LL; - exp_out--; + if (result == minMantissa - 1LL) + result += 1LL; + else + { + result *= 10LL; + exp_out--; + } } // likewise the value can fall above the maxMantissa if (result > maxMantissa) { - // if it does push it down - result /= 10LL; - exp_out++; + if (result == maxMantissa + 1LL) + result -= 1LL; + else + { + result /= 10LL; + exp_out++; + } } exp_out -= 15; diff --git a/src/test/app/SetHook_test.cpp b/src/test/app/SetHook_test.cpp index 53452f2d7..efc51322d 100644 --- a/src/test/app/SetHook_test.cpp +++ b/src/test/app/SetHook_test.cpp @@ -2031,6 +2031,204 @@ public: void test_float_divide() { + testcase("Test float_divide"); + using namespace jtx; + Env env{*this, supported_amendments()}; + + auto const alice = Account{"alice"}; + auto const bob = Account{"bob"}; + env.fund(XRP(10000), alice); + env.fund(XRP(10000), bob); + + { + TestHook + hook = + wasm[R"[test.hook]( + #include + extern int32_t _g (uint32_t id, uint32_t maxiter); + #define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1) + extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code); + extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code); + extern int64_t float_divide (int64_t, int64_t); + extern int64_t float_one (void); + #define INVALID_FLOAT -10024 + #define DIVISION_BY_ZERO -25 + #define XFL_OVERFLOW -30 + #define ASSERT(x)\ + if (!(x))\ + rollback(0,0,__LINE__); + extern int64_t float_compare(int64_t, int64_t, uint32_t); + extern int64_t float_negate(int64_t); + extern int64_t float_sum(int64_t, int64_t); + extern int64_t float_mantissa(int64_t); + extern int64_t float_exponent(int64_t); + #define ASSERT_EQUAL(x, y)\ + {\ + int64_t px = (x);\ + int64_t py = (y);\ + int64_t mx = float_mantissa(px);\ + int64_t my = float_mantissa(py);\ + int32_t diffexp = float_exponent(px) - float_exponent(py);\ + if (diffexp == 1)\ + mx *= 10LL;\ + if (diffexp == -1)\ + my *= 10LL;\ + int64_t diffman = mx - my;\ + if (diffman < 0) diffman *= -1LL;\ + if (diffexp < 0) diffexp *= -1;\ + if (diffexp > 1 || diffman > 5000000)\ + rollback((uint32_t) #x, sizeof(#x), __LINE__);\ + } + int64_t hook(uint32_t reserved ) + { + _g(1,1); + + // ensure invalid xfl are not accepted + ASSERT(float_divide(-1, float_one()) == INVALID_FLOAT); + + // divide by 0 + ASSERT(float_divide(float_one(), 0) == DIVISION_BY_ZERO); + ASSERT(float_divide(0, float_one()) == 0); + + // check 1 + ASSERT(float_divide(float_one(), float_one()) == float_one()); + ASSERT(float_divide(float_one(), float_negate(float_one())) == float_negate(float_one())); + ASSERT(float_divide(float_negate(float_one()), float_one()) == float_negate(float_one())); + ASSERT(float_divide(float_negate(float_one()), float_negate(float_one())) == float_one()); + + // 1 / 10 = 0.1 + ASSERT_EQUAL(float_divide(float_one(), 6107881094714392576LL), 6071852297695428608LL); + + // 123456789 / 1623 = 76067.0295749 + ASSERT_EQUAL(float_divide(6234216452170766464LL, 6144532891733356544LL), 6168530993200328528LL); + + // -1.245678451111 / 1.3546984132111e+42 = -9.195245517106014e-43 + ASSERT_EQUAL(float_divide(1478426356228633688LL, 6846826132016365020LL), 711756787386903390LL); + + // 9.134546514878452e-81 / 1 + ASSERT(float_divide(4638834963451748340LL, float_one()) == 4638834963451748340LL); + + // 9.134546514878452e-81 / 1.41649684651e+75 = (underflow 0) + ASSERT(float_divide(4638834963451748340LL, 7441363081262569392LL) == 0); + + // 1.3546984132111e+42 / 9.134546514878452e-81 = XFL_OVERFLOW + ASSERT(float_divide(6846826132016365020LL, 4638834963451748340LL) == XFL_OVERFLOW); + + ASSERT_EQUAL( + float_divide( + 3121244226425810900LL /* -4.753284285427668e+91 */, + 2135203055881892282LL /* -9.50403176301817e+36 */), + 7066645550312560102LL /* 5.001334595622374e+54 */); + ASSERT_EQUAL( + float_divide( + 2473507938381460320LL /* -5.535342582428512e+55 */, + 6365869885731270068LL /* 6787211884129716 */), + 2187897766692155363LL /* -8.155547044835299e+39 */); + ASSERT_EQUAL( + float_divide( + 1716271542690607496LL /* -49036842898190.16 */, + 3137794549622534856LL /* -3.28920897266964e+92 */), + 4667220053951274769LL /* 1.490839995440913e-79 */); + ASSERT_EQUAL( + float_divide( + 1588045991926420391LL /* -2778923.092005799 */, + 5933338827267685794LL /* 6.601717648113058e-9 */), + 1733591650950017206LL /* -420939403974674.2 */); + ASSERT_EQUAL( + float_divide( + 5880783758174228306LL /* 8.089844083101523e-12 */, + 1396720886139976383LL /* -0.00009612200909863615 */), + 1341481714205255877LL /* -8.416224503589061e-8 */); + ASSERT_EQUAL( + float_divide( + 5567703563029955929LL /* 1.254423600022873e-29 */, + 2184969513100691140LL /* -5.227293453371076e+39 */), + 236586937995245543LL /* -2.399757371979751e-69 */); + ASSERT_EQUAL( + float_divide( + 7333313065548121054LL /* 1.452872188953566e+69 */, + 1755926008837497886LL /* -8529353417745438 */), + 2433647177826281173LL /* -1.703379046213333e+53 */); + ASSERT_EQUAL( + float_divide( + 1172441975040622050LL /* -1.50607192429309e-17 */, + 6692015311011173216LL /* 8.673463993357152e+33 */), + 560182767210134346LL /* -1.736413416192842e-51 */); + ASSERT_EQUAL( + float_divide( + 577964843368607493LL /* -1.504091065184005e-50 */, + 6422931182144699580LL /* 9805312769113276000 */), + 235721135837751035LL /* -1.533955214485243e-69 */); + ASSERT_EQUAL( + float_divide( + 6039815413139899240LL /* 0.0049919124634346 */, + 2117655488444284242LL /* -9.970862834892113e+35 */), + 779625635892827768LL /* -5.006499985102456e-39 */); + ASSERT_EQUAL( + float_divide( + 1353563835098586141LL /* -2.483946887437341e-7 */, + 6450909070545770298LL /* 175440415122002600000 */), + 992207753070525611LL /* -1.415835049016491e-27 */); + ASSERT_EQUAL( + float_divide( + 6382158843584616121LL /* 50617712279937850 */, + 5373794957212741595LL /* 5.504201387110363e-40 */), + 7088854809772330055LL /* 9.196195545910343e+55 */); + ASSERT_EQUAL( + float_divide( + 2056891719200540975LL /* -3.250289119594799e+32 */, + 1754532627802542730LL /* -7135972382790282 */), + 6381651867337939070LL /* 45547949813167340 */); + ASSERT_EQUAL( + float_divide( + 5730152450208688630LL /* 1.573724193417718e-20 */, + 1663581695074866883LL /* -62570322025.24355 */), + 921249452789827075LL /* -2.515128806245891e-31 */); + ASSERT_EQUAL( + float_divide( + 6234301156018475310LL /* 131927173.7708846 */, + 2868710604383082256LL /* -4.4212413754468e+77 */), + 219156721749007916LL /* -2.983939635224108e-70 */); + ASSERT_EQUAL( + float_divide( + 2691125731495874243LL /* -6.980353583058627e+67 */, + 7394070851520237320LL /* 8.16746263262388e+72 */), + 1377640825464715759LL /* -0.000008546538744084975 */); + ASSERT_EQUAL( + float_divide( + 5141867696142208039LL /* 7.764120939842599e-53 */, + 5369434678231981897LL /* 1.143922406350665e-40 */), + 5861466794943198400LL /* 6.7872793615536e-13 */); + ASSERT_EQUAL( + float_divide( + 638296190872832492LL /* -7.792243040963052e-47 */, + 5161669734904371378LL /* 9.551761192523954e-52 */), + 1557396184145861422LL /* -81579.12330410798 */); + ASSERT_EQUAL( + float_divide( + 2000727145906286285LL /* -1.128911353786061e+29 */, + 2096625200460673392LL /* -6.954973360763248e+34 */), + 5982403476503576795LL /* 0.000001623171355558107 */); + ASSERT_EQUAL( + float_divide( + 640472838055334326LL /* -9.968890223464885e-47 */, + 5189754252349396763LL /* 1.607481618585371e-50 */), + 1537425431139169736LL /* -6201.557833201096 */); + return + accept(0,0,0); + } + )[test.hook]"]; + + env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0), + M("set float_divide"), + HSFEE); + env.close(); + + env(pay(bob, alice, XRP(1)), + M("test float_divide"), + fee(XRP(1))); + env.close(); + } } void @@ -2208,15 +2406,24 @@ public: extern int64_t float_compare(int64_t, int64_t, uint32_t); extern int64_t float_negate(int64_t); extern int64_t float_sum(int64_t, int64_t); - #define ASSERT_EQUAL(x,y)\ + extern int64_t float_mantissa(int64_t); + extern int64_t float_exponent(int64_t); + #define ASSERT_EQUAL(x, y)\ {\ int64_t px = (x);\ - int64_t ny = float_negate((y));\ - int64_t diff = float_sum(px, ny);\ - if (float_compare(diff, 0, 2))\ - diff = float_negate(diff);\ - if (float_compare(diff, 5999794703657500672LL /* 10**-5 */, 4))\ - rollback((uint32_t)#x,sizeof(#x), __LINE__);\ + int64_t py = (y);\ + int64_t mx = float_mantissa(px);\ + int64_t my = float_mantissa(py);\ + int32_t diffexp = float_exponent(px) - float_exponent(py);\ + if (diffexp == 1)\ + mx *= 10LL;\ + if (diffexp == -1)\ + my *= 10LL;\ + int64_t diffman = mx - my;\ + if (diffman < 0) diffman *= -1LL;\ + if (diffexp < 0) diffexp *= -1;\ + if (diffexp > 1 || diffman > 5000000)\ + rollback((uint32_t) #x, sizeof(#x), __LINE__);\ } int64_t hook(uint32_t reserved ) { @@ -2282,20 +2489,26 @@ public: extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code); extern int64_t float_log (int64_t float1); extern int64_t float_one (void); - extern int64_t float_compare(int64_t, int64_t, uint32_t); - extern int64_t float_negate(int64_t); - extern int64_t float_sum(int64_t, int64_t); #define INVALID_ARGUMENT -7 #define COMPLEX_NOT_SUPPORTED -39 - #define ASSERT_EQUAL(x,y)\ + extern int64_t float_mantissa(int64_t); + extern int64_t float_exponent(int64_t); + #define ASSERT_EQUAL(x, y)\ {\ int64_t px = (x);\ - int64_t ny = float_negate((y));\ - int64_t diff = float_sum(px, ny);\ - if (float_compare(diff, 0, 2))\ - diff = float_negate(diff);\ - if (float_compare(diff, 5189146770730811392LL /* 10**-50 */, 4))\ - rollback(0,0, __LINE__);\ + int64_t py = (y);\ + int64_t mx = float_mantissa(px);\ + int64_t my = float_mantissa(py);\ + int32_t diffexp = float_exponent(px) - float_exponent(py);\ + if (diffexp == 1)\ + mx *= 10LL;\ + if (diffexp == -1)\ + my *= 10LL;\ + int64_t diffman = mx - my;\ + if (diffman < 0) diffman *= -1LL;\ + if (diffexp < 0) diffexp *= -1;\ + if (diffexp > 1 || diffman > 5000000)\ + rollback((uint32_t) #x, sizeof(#x), __LINE__);\ } int64_t hook(uint32_t reserved ) { @@ -2506,15 +2719,24 @@ public: extern int64_t float_compare(int64_t, int64_t, uint32_t); extern int64_t float_negate(int64_t); extern int64_t float_sum(int64_t, int64_t); - #define ASSERT_EQUAL(x,y)\ + extern int64_t float_mantissa(int64_t); + extern int64_t float_exponent(int64_t); + #define ASSERT_EQUAL(x, y)\ {\ int64_t px = (x);\ - int64_t ny = float_negate((y));\ - int64_t diff = float_sum(px, ny);\ - if (float_compare(diff, 0, 2))\ - diff = float_negate(diff);\ - if (float_compare(diff, 5189146770730811392LL /* 10**-50 */, 4))\ - rollback(0,0, __LINE__);\ + int64_t py = (y);\ + int64_t mx = float_mantissa(px);\ + int64_t my = float_mantissa(py);\ + int32_t diffexp = float_exponent(px) - float_exponent(py);\ + if (diffexp == 1)\ + mx *= 10LL;\ + if (diffexp == -1)\ + my *= 10LL;\ + int64_t diffman = mx - my;\ + if (diffman < 0) diffman *= -1LL;\ + if (diffexp < 0) diffexp *= -1;\ + if (diffexp > 1 || diffman > 5000000)\ + rollback((uint32_t) #x, sizeof(#x), __LINE__);\ } int64_t hook(uint32_t reserved ) { @@ -2603,15 +2825,24 @@ public: extern int64_t float_compare(int64_t, int64_t, uint32_t); extern int64_t float_negate(int64_t); extern int64_t float_sum(int64_t, int64_t); - #define ASSERT_EQUAL(x,y)\ + extern int64_t float_mantissa(int64_t); + extern int64_t float_exponent(int64_t); + #define ASSERT_EQUAL(x, y)\ {\ int64_t px = (x);\ - int64_t ny = float_negate((y));\ - int64_t diff = float_sum(px, ny);\ - if (float_compare(diff, 0, 2))\ - diff = float_negate(diff);\ - if (float_compare(diff, 5189146770730811392LL /* 10**-50 */, 4))\ - rollback(0,0, __LINE__);\ + int64_t py = (y);\ + int64_t mx = float_mantissa(px);\ + int64_t my = float_mantissa(py);\ + int32_t diffexp = float_exponent(px) - float_exponent(py);\ + if (diffexp == 1)\ + mx *= 10LL;\ + if (diffexp == -1)\ + my *= 10LL;\ + int64_t diffman = mx - my;\ + if (diffman < 0) diffman *= -1LL;\ + if (diffexp < 0) diffexp *= -1;\ + if (diffexp > 1 || diffman > 5000000)\ + rollback((uint32_t) #x, sizeof(#x), __LINE__);\ } int64_t hook(uint32_t reserved ) { @@ -2634,6 +2865,108 @@ public: 6387097057170171072LL, float_sum(1676857706508234512LL, 6396111470866104320LL)); + // auto generated random sums + ASSERT_EQUAL( + float_sum( + 95785354843184473 /* -5.713362295774553e-77 */, + 7607324992379065667 /* 5.248821377668419e+84 */), + 7607324992379065667 /* 5.248821377668419e+84 */); + ASSERT_EQUAL( + float_sum( + 1011203427860697296 /* -2.397111329706192e-26 */, + 7715811566197737722 /* 5.64900413944857e+90 */), + 7715811566197737722 /* 5.64900413944857e+90 */); + ASSERT_EQUAL( + float_sum( + 6507979072644559603 /* 4.781210721563379e+23 */, + 422214339164556094 /* -7.883173446470462e-59 */), + 6507979072644559603 /* 4.781210721563379e+23 */); + ASSERT_EQUAL( + float_sum( + 129493221419941559 /* -3.392431853567671e-75 */, + 6742079437952459317 /* 4.694395406197301e+36 */), + 6742079437952459317 /* 4.694395406197301e+36 */); + ASSERT_EQUAL( + float_sum( + 5172806703808250354 /* 2.674331586920946e-51 */, + 3070396690523275533 /* -7.948943911338253e+88 */), + 3070396690523275533 /* -7.948943911338253e+88 */); + ASSERT_EQUAL( + float_sum( + 2440992231195047997 /* -9.048432414980156e+53 */, + 4937813945440933271 /* 1.868753842869655e-64 */), + 2440992231195047996 /* -9.048432414980156e+53 */); + ASSERT_EQUAL( + float_sum( + 7351918685453062372 /* 2.0440935844129e+70 */, + 6489541496844182832 /* 4.358033430668592e+22 */), + 7351918685453062372 /* 2.0440935844129e+70 */); + ASSERT_EQUAL( + float_sum( + 4960621423606196948 /* 6.661833498651348e-63 */, + 6036716382996689576 /* 0.001892882320224936 */), + 6036716382996689576 /* 0.001892882320224936 */); + ASSERT_EQUAL( + float_sum( + 1342689232407435206 /* -9.62374270576839e-8 */, + 5629833007898276923 /* 9.340672939897915e-26 */), + 1342689232407435206 /* -9.62374270576839e-8 */); + ASSERT_EQUAL( + float_sum( + 7557687707019793516 /* 9.65473154684222e+81 */, + 528084028396448719 /* -5.666471621471183e-53 */), + 7557687707019793516 /* 9.65473154684222e+81 */); + ASSERT_EQUAL( + float_sum( + 130151633377050812 /* -4.050843810676924e-75 */, + 2525286695563827336 /* -3.270904236349576e+58 */), + 2525286695563827336 /* -3.270904236349576e+58 */); + ASSERT_EQUAL( + float_sum( + 5051914485221832639 /* 7.88290256687712e-58 */, + 7518727241611221951 /* 6.723063157234623e+79 */), + 7518727241611221951 /* 6.723063157234623e+79 */); + ASSERT_EQUAL( + float_sum( + 3014788764095798870 /* -6.384213012307542e+85 */, + 7425019819707800346 /* 3.087633801222938e+74 */), + 3014788764095767995 /* -6.384213012276667e+85 */); + ASSERT_EQUAL( + float_sum( + 4918950856932792129 /* 1.020063844210497e-65 */, + 7173510242188034581 /* 3.779635414204949e+60 */), + 7173510242188034581 /* 3.779635414204949e+60 */); + ASSERT_EQUAL( + float_sum( + 20028000442705357 /* -2.013601933223373e-81 */, + 95248745393457140 /* -5.17675284604722e-77 */), + 95248946753650462 /* -5.176954206240542e-77 */); + ASSERT_EQUAL( + float_sum( + 5516870225060928024 /* 4.46428115944092e-32 */, + 7357202055584617194 /* 7.327463715967722e+70 */), + 7357202055584617194 /* 7.327463715967722e+70 */); + ASSERT_EQUAL( + float_sum( + 2326103538819088036 /* -2.2461310959121e+47 */, + 1749360946246242122 /* -1964290826489674 */), + 2326103538819088036 /* -2.2461310959121e+47 */); + ASSERT_EQUAL( + float_sum( + 1738010758208819410 /* -862850129854894.6 */, + 2224610859005732191 /* -8.83984233944816e+41 */), + 2224610859005732192 /* -8.83984233944816e+41 */); + ASSERT_EQUAL( + float_sum( + 4869534730307487904 /* 5.647132747352224e-68 */, + 2166841923565712115 /* -5.114102427874035e+38 */), + 2166841923565712115 /* -5.114102427874035e+38 */); + ASSERT_EQUAL( + float_sum( + 1054339559322014937 /* -9.504445772059864e-24 */, + 1389511416678371338 /* -0.0000240273144825857 */), + 1389511416678371338 /* -0.0000240273144825857 */); + return accept(0,0,0); }