fix multiplication by using cpp_int

This commit is contained in:
Richard Holland
2022-11-11 13:44:11 +00:00
parent 467d8bd3ee
commit 771af44e09
2 changed files with 342 additions and 69 deletions

View File

@@ -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(

View File

@@ -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