mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 09:17:53 +00:00
fix multiplication by using cpp_int
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
#include <utility>
|
||||
#include <wasmedge/wasmedge.h>
|
||||
#include <ripple/protocol/tokens.h>
|
||||
#include <boost/multiprecision/cpp_dec_float.hpp>
|
||||
|
||||
using namespace ripple;
|
||||
|
||||
@@ -405,8 +406,6 @@ namespace hook_float
|
||||
|
||||
inline int64_t make_float(uint64_t mantissa, int32_t exponent, bool neg)
|
||||
{
|
||||
assert(mantissa > 0);
|
||||
|
||||
if (mantissa == 0)
|
||||
return 0;
|
||||
if (mantissa > maxMantissa)
|
||||
@@ -433,6 +432,7 @@ namespace hook_float
|
||||
template <typename T>
|
||||
inline int64_t normalize_xfl(T& man, int32_t& exp, bool neg = false)
|
||||
{
|
||||
|
||||
constexpr bool sman = std::is_same<T, int64_t>::value;
|
||||
static_assert(sman || std::is_same<T, uint64_t>());
|
||||
|
||||
@@ -447,6 +447,7 @@ namespace hook_float
|
||||
int32_t mo = log10(man);
|
||||
int32_t adjust = 15 - mo;
|
||||
|
||||
printf("normalize_xfl: man: %llu exp: %d mo: %d adjust: %d\n", man, exp, mo, adjust);
|
||||
if (adjust > 0)
|
||||
{
|
||||
man *= power_of_ten[adjust];
|
||||
@@ -454,8 +455,8 @@ namespace hook_float
|
||||
}
|
||||
else if (adjust < 0)
|
||||
{
|
||||
man /= power_of_ten[adjust];
|
||||
exp += adjust;
|
||||
man /= power_of_ten[-adjust];
|
||||
exp -= adjust;
|
||||
}
|
||||
|
||||
if (man == 0)
|
||||
@@ -512,6 +513,7 @@ namespace hook_float
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4154,9 +4156,7 @@ DEFINE_HOOK_FUNCTION(
|
||||
if (man < minMantissa || man > maxMantissa || exp < minExponent || exp > maxExponent)
|
||||
RETURN_HOOK_TRACE(read_ptr, read_len, "Float <INVALID>");
|
||||
|
||||
man *= (neg ? -1 : 1);
|
||||
|
||||
RETURN_HOOK_TRACE(read_ptr, read_len, "Float " << man << "*10^(" << exp << ")");
|
||||
RETURN_HOOK_TRACE(read_ptr, read_len, "Float " << (neg ? "-" : "") << man << "*10^(" << exp << ")");
|
||||
}
|
||||
|
||||
DEFINE_HOOK_FUNCTION(
|
||||
@@ -4177,25 +4177,6 @@ DEFINE_HOOK_FUNCTION(
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/31652875/fastest-way-to-multiply-two-64-bit-ints-to-128-bit-then-to-64-bit
|
||||
inline void umul64wide (uint64_t a, uint64_t b, uint64_t *hi, uint64_t *lo)
|
||||
{
|
||||
uint64_t a_lo = (uint64_t)(uint32_t)a;
|
||||
uint64_t a_hi = a >> 32;
|
||||
uint64_t b_lo = (uint64_t)(uint32_t)b;
|
||||
uint64_t b_hi = b >> 32;
|
||||
|
||||
uint64_t p0 = a_lo * b_lo;
|
||||
uint64_t p1 = a_lo * b_hi;
|
||||
uint64_t p2 = a_hi * b_lo;
|
||||
uint64_t p3 = a_hi * b_hi;
|
||||
|
||||
uint32_t cy = (uint32_t)(((p0 >> 32) + (uint32_t)p1 + (uint32_t)p2) >> 32);
|
||||
|
||||
*lo = p0 + (p1 << 32) + (p2 << 32);
|
||||
*hi = p3 + (p1 >> 32) + (p2 >> 32) + cy;
|
||||
}
|
||||
|
||||
inline int64_t mulratio_internal
|
||||
(int64_t& man1, int32_t& exp1, bool round_up, uint32_t numerator, uint32_t denominator)
|
||||
{
|
||||
@@ -4221,50 +4202,19 @@ inline int64_t float_multiply_internal_parts(
|
||||
int32_t exp2,
|
||||
bool neg2)
|
||||
{
|
||||
int32_t exp_out = exp1 + exp2;
|
||||
using namespace boost::multiprecision;
|
||||
cpp_int mult = cpp_int(man1) * cpp_int(man2);
|
||||
mult /= power_of_ten[15];
|
||||
uint64_t man_out = static_cast<uint64_t>(mult);
|
||||
int32_t exp_out = exp1 + exp2 + 15;
|
||||
bool neg_out = (neg1 && !neg2) || (!neg1 && neg2);
|
||||
int64_t ret = normalize_xfl(man_out, exp_out, neg_out);
|
||||
|
||||
// multiply the mantissas, this could result in upto a 128 bit number, represented as high and low here
|
||||
uint64_t man_hi = 0, man_lo = 0;
|
||||
umul64wide(man1, man2, &man_hi, &man_lo);
|
||||
|
||||
// normalize our double wide mantissa by shifting bits under man_hi is 0
|
||||
uint8_t man_shifted = 0;
|
||||
while (man_hi > 0)
|
||||
{
|
||||
bool set = (man_hi & 1) != 0;
|
||||
man_hi >>= 1;
|
||||
man_lo >>= 1;
|
||||
man_lo += (set ? (1ULL<<63U) : 0);
|
||||
man_shifted++;
|
||||
}
|
||||
|
||||
// we shifted the mantissa by man_shifted bits, which equates to a division by 2^man_shifted
|
||||
// now shift into the normalized range
|
||||
while (man_lo > maxMantissa)
|
||||
{
|
||||
if (exp_out > maxExponent)
|
||||
if (ret == EXPONENT_UNDERSIZED)
|
||||
return 0;
|
||||
if (ret == EXPONENT_OVERSIZED)
|
||||
return XFL_OVERFLOW;
|
||||
man_lo /= 10;
|
||||
exp_out++;
|
||||
}
|
||||
|
||||
// we can adjust for the bitshifting by doing upto two smaller multiplications now
|
||||
neg1 = (neg1 && !neg2) || (!neg1 && neg2);
|
||||
int64_t man_out = (neg1 ? -1LL : 1LL) * ((int64_t)(man_lo));
|
||||
if (man_shifted > 32)
|
||||
{
|
||||
man_shifted -=32;
|
||||
if (mulratio_internal(man_out, exp_out, false, 0xFFFFFFFFU, 1) < 0)
|
||||
return XFL_OVERFLOW;
|
||||
}
|
||||
|
||||
if (mulratio_internal(man_out, exp_out, false, 1U << man_shifted, 1) < 0)
|
||||
return XFL_OVERFLOW;
|
||||
|
||||
if (man_out < 0)
|
||||
man_out *= -1LL;
|
||||
// now we have our product
|
||||
return make_float(man_out, exp_out, neg1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_HOOK_FUNCTION(
|
||||
|
||||
@@ -2556,6 +2556,134 @@ public:
|
||||
void
|
||||
test_float_mantissa()
|
||||
{
|
||||
testcase("Test float_mantissa");
|
||||
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 <stdint.h>
|
||||
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_one (void);
|
||||
extern int64_t float_mantissa(int64_t);
|
||||
extern int64_t float_negate(int64_t);
|
||||
#define ASSERT_EQUAL(x,y)\
|
||||
if ((x) != (y))\
|
||||
rollback(0,0,__LINE__);
|
||||
#define ASSERT(x)\
|
||||
if (!(x))\
|
||||
rollback(0,0,__LINE__);
|
||||
#define INVALID_FLOAT -10024
|
||||
int64_t hook(uint32_t reserved )
|
||||
{
|
||||
_g(1,1);
|
||||
|
||||
int64_t result = 0;
|
||||
|
||||
// test invalid floats
|
||||
{
|
||||
ASSERT(float_mantissa(-1) == INVALID_FLOAT);
|
||||
ASSERT(float_mantissa(-11010191919LL) == INVALID_FLOAT);
|
||||
}
|
||||
|
||||
// test canonical zero
|
||||
ASSERT(float_mantissa(0) == 0);
|
||||
|
||||
// test one, negative one
|
||||
{
|
||||
ASSERT(float_mantissa(float_one()) == 1000000000000000LL);
|
||||
ASSERT(float_mantissa(float_negate(float_one())) == 1000000000000000LL);
|
||||
}
|
||||
|
||||
// test random numbers
|
||||
{
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(4763370308433150973LL /* 7.569101929907197e-74 */),
|
||||
7569101929907197LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(668909658849475214LL /* -2.376913998641806e-45 */),
|
||||
2376913998641806LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(962271544155031248LL /* -7.508423152486096e-29 */),
|
||||
7508423152486096LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(7335644976228470276LL /* 3.784782869302788e+69 */),
|
||||
3784782869302788LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(2837780149340315954LL /* -9.519583351644467e+75 */),
|
||||
9519583351644466LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(2614004940018599738LL /* -1.917156143712058e+63 */),
|
||||
1917156143712058LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(4812250541755005603LL /* 2.406139723315875e-71 */),
|
||||
2406139723315875LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(5140304866732560580LL /* 6.20129153019514e-53 */),
|
||||
6201291530195140LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(1124677839589482624LL /* -7.785132001599617e-20 */),
|
||||
7785132001599616LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(5269336076015865585LL /* 9.131711247126257e-46 */),
|
||||
9131711247126257LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(2296179634826760368LL /* -8.3510241225484e+45 */),
|
||||
8351024122548400LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(1104028240398536470LL /* -5.149931320135446e-21 */),
|
||||
5149931320135446LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(2691222059222981864LL /* -7.076681310166248e+67 */),
|
||||
7076681310166248LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(6113256168823855946LL /* 63.7507410946337 */),
|
||||
6375074109463370LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(311682216630003626LL /* -5.437441968809898e-65 */),
|
||||
5437441968809898LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(794955605753965262LL /* -2.322071336757966e-38 */),
|
||||
2322071336757966LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(204540636400815950LL /* -6.382252796514126e-71 */),
|
||||
6382252796514126LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(5497195278343034975LL /* 2.803732951029855e-33 */),
|
||||
2803732951029855LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(1450265914369875626LL /* -0.09114033611316906 */),
|
||||
9114033611316906LL);
|
||||
ASSERT_EQUAL(
|
||||
float_mantissa(7481064015089962668LL /* 5.088633654939308e+77 */),
|
||||
5088633654939308LL);
|
||||
}
|
||||
|
||||
return
|
||||
accept(0,0,0);
|
||||
}
|
||||
)[test.hook]"];
|
||||
|
||||
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
||||
M("set float_mantissa"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
env(pay(bob, alice, XRP(1)),
|
||||
M("test float_mantissa"),
|
||||
fee(XRP(1)));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2571,6 +2699,201 @@ public:
|
||||
void
|
||||
test_float_multiply()
|
||||
{
|
||||
testcase("Test float_multiply");
|
||||
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 <stdint.h>
|
||||
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_multiply (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);
|
||||
#define float_exponent(f) (((int32_t)(((f) >> 54U) & 0xFFU)) - 97)
|
||||
#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 || mx < 0 || my < 0)\
|
||||
rollback((uint32_t) #x, sizeof(#x), __LINE__);\
|
||||
}
|
||||
int64_t hook(uint32_t reserved )
|
||||
{
|
||||
_g(1,1);
|
||||
|
||||
// ensure invalid xfl are not accepted
|
||||
ASSERT(float_multiply(-1, float_one()) == INVALID_FLOAT);
|
||||
|
||||
// multiply by 0
|
||||
ASSERT(float_multiply(float_one(), 0) == 0);
|
||||
ASSERT(float_multiply(0, float_one()) == 0);
|
||||
|
||||
// check 1
|
||||
ASSERT(float_multiply(float_one(), float_one()) == float_one());
|
||||
ASSERT(float_multiply(float_one(), float_negate(float_one())) == float_negate(float_one()));
|
||||
ASSERT(float_multiply(float_negate(float_one()), float_one()) == float_negate(float_one()));
|
||||
ASSERT(float_multiply(float_negate(float_one()), float_negate(float_one())) == float_one());
|
||||
|
||||
// check overflow
|
||||
// 1e+95 * 1e+95
|
||||
ASSERT(float_multiply(7801234554605699072LL, 7801234554605699072LL) == XFL_OVERFLOW);
|
||||
// 1e+95 * 10
|
||||
ASSERT(float_multiply(7801234554605699072LL, 6107881094714392576LL) == XFL_OVERFLOW);
|
||||
ASSERT(float_multiply(6107881094714392576LL, 7801234554605699072LL) == XFL_OVERFLOW);
|
||||
// -1e+95 * 10
|
||||
ASSERT(float_multiply(3189548536178311168LL, 6107881094714392576LL) == XFL_OVERFLOW);
|
||||
|
||||
// identity
|
||||
ASSERT_EQUAL(float_multiply(3189548536178311168LL, float_one()), 3189548536178311168LL);
|
||||
ASSERT_EQUAL(float_multiply(float_one(), 3189548536178311168LL), 3189548536178311168LL);
|
||||
|
||||
|
||||
// random multiplications
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
7791757438262485039LL /* 9.537282166267951e+94 */,
|
||||
4759088999670263908LL /* 3.287793167020132e-74 */),
|
||||
6470304726017852129LL /* 3.135661113819873e+21 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
7534790022873909775LL /* 4.771445910440463e+80 */,
|
||||
1017891960669847079LL /* -9.085644138855975e-26 */),
|
||||
2472307761756037979LL /* -4.335165957006171e+55 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
2813999069907898454LL /* -3.75290242870895e+74 */,
|
||||
4962524721184225460LL /* 8.56513107667986e-63 */),
|
||||
1696567870013294731LL /* -3214410121988.235 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
2151742066453140308LL /* -8.028643824784212e+37 */,
|
||||
437647738130579252LL /* -5.302173903011636e-58 */),
|
||||
5732835652591705549LL /* 4.256926576434637e-20 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
5445302332922546340LL /* 4.953983058987172e-36 */,
|
||||
7770966530708354172LL /* 6.760773121619068e+93 */),
|
||||
7137051085305881332LL /* 3.349275551015668e+58 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
2542989542826132533LL /* -2.959352989172789e+59 */,
|
||||
6308418769944702613LL /* 3379291626008.213 */),
|
||||
2775217422137696934LL /* -1.000051677471398e+72 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
5017652318929433511LL /* 9.649533293441959e-60 */,
|
||||
6601401767766764916LL /* 8.131913296358772e+28 */),
|
||||
5538267259220228820LL /* 7.846916809259732e-31 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
892430323307269235LL /* -9.724796342652019e-33 */,
|
||||
1444078017997143500LL /* -0.0292613723858478 */),
|
||||
5479222755754111850LL /* 2.845608871588714e-34 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
7030632722283214253LL /* 5.017303585240493e+52 */,
|
||||
297400838197636668LL /* -9.170462045924924e-66 */),
|
||||
1247594596364389994LL /* -4.601099210133098e-13 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
1321751204165279730LL /* -6.700112973094898e-9 */,
|
||||
2451801790748530375LL /* -1.843593458980551e+54 */),
|
||||
6918764256086244704LL /* 1.235228445162848e+46 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
2055496484261758590LL /* -1.855054180812414e+32 */,
|
||||
2079877890137711361LL /* -8.222061547283201e+33 */),
|
||||
7279342234795540005LL /* 1.525236964818469e+66 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
2439875962311968674LL /* -7.932163531900834e+53 */,
|
||||
4707485682591872793LL /* 5.727671617074969e-77 */),
|
||||
1067392794851803610LL /* -4.543282792366554e-23 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
6348574818322812800LL /* 750654298515443.2 */,
|
||||
6474046245013515838LL /* 6.877180109483582e+21 */),
|
||||
6742547427357110773LL /* 5.162384810848757e+36 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
1156137305783593424LL /* -3.215801176746448e-18 */,
|
||||
351790564990861307LL /* -9.516993310703611e-63 */),
|
||||
4650775291275116747LL /* 3.060475828764875e-80 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
5786888485280994123LL /* 4.266563737277259e-17 */,
|
||||
6252137323085080394LL /* 1141040294.831946 */),
|
||||
5949619829273756852LL /* 4.868321144702132e-8 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
2078182880999439640LL /* -6.52705240901148e+33 */,
|
||||
1662438186251269392LL /* -51135233789.26864 */),
|
||||
6884837854131013998LL /* 3.33762350889611e+44 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
1823781083140711248LL /* -43268336830308640000 */,
|
||||
1120252241608199010LL /* -3.359534020316002e-20 */),
|
||||
6090320310700749729LL /* 1.453614495839137 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
6617782604883935174LL /* 6.498351904047046e+29 */,
|
||||
6185835042802056262LL /* 689635.404973575 */),
|
||||
6723852137583788319LL /* 4.481493547008287e+35 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
333952667495151166LL /* -9.693494324475454e-64 */,
|
||||
1556040883317758614LL /* -68026.1150230799 */),
|
||||
5032611291744396930LL /* 6.594107598923394e-59 */);
|
||||
ASSERT_EQUAL(
|
||||
float_multiply(
|
||||
2326968399632616779LL /* -3.110991909440843e+47 */,
|
||||
707513695207834635LL /* -4.952153338037259e-43 */),
|
||||
6180479299649214949LL /* 154061.0896894437 */);
|
||||
return
|
||||
accept(0,0,0);
|
||||
}
|
||||
)[test.hook]"];
|
||||
|
||||
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
||||
M("set float_multiply"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
env(pay(bob, alice, XRP(1)),
|
||||
M("test float_multiply"),
|
||||
fee(XRP(1)));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user