Skip to content
232 changes: 202 additions & 30 deletions curation_calculator.html
Original file line number Diff line number Diff line change
Expand Up @@ -448,19 +448,21 @@ <h3>

//Functions for Approx SQRT (see https://github.com/steemit/steem/blob/5c787c5baa651858658caa2bd47473846b099c0a/doc/sqrt.md)
function approx_sqrt(x){
if(x == 0) return 0;
msb_x = find_msb(x);
msb_z = msb_x >> 1;
// we don't want to let the code aproximate sqrts from negative inputs..
if(x <= 0) return 0;

var msb_x = find_msb(x);
var msb_z = msb_x >> 1;

msb_x_bit = shiftleft64(1,msb_x);
msb_z_bit = shiftleft64(1,msb_z);
var msb_x_bit = shiftleft64(1,msb_x);
var msb_z_bit = shiftleft64(1,msb_z);

mantissa_mask = msb_x_bit - 1;
mantissa_x = and64(x,mantissa_mask);
mantissa_z_hi = and64(msb_x,1) ? msb_z_bit : 0;
mantissa_z_lo = shiftright64(mantissa_x , msb_x - msb_z);
mantissa_z = shiftright64(or64(mantissa_z_hi , mantissa_z_lo) , 1);
result = or64(msb_z_bit , mantissa_z);
var mantissa_mask = msb_x_bit - 1;
var mantissa_x = and64(x,mantissa_mask);
var mantissa_z_hi = and64(msb_x,1) ? msb_z_bit : 0;
var mantissa_z_lo = shiftright64(mantissa_x , msb_x - msb_z);
var mantissa_z = shiftright64(or64(mantissa_z_hi , mantissa_z_lo) , 1);
var result = or64(msb_z_bit , mantissa_z);

return result;
}
Expand All @@ -469,43 +471,213 @@ <h3>
return Math.floor(Math.log(x)/Math.log(2));
}

const BIT32_LIMIT = Math.pow(2,32);
const BIT64_LIMIT = Math.pow(2,64);

function hi_lo(x){
r = {};
r.hi = Math.floor(x / Math.pow(2,32));
r.lo = x % Math.pow(2,32);
var r = {};
r.hi = Math.floor(x / BIT32_LIMIT);
r.lo = x % BIT32_LIMIT;
return r;
}

function and64(x,y){
x = hi_lo(x);
y = hi_lo(y);
hi = x.hi & y.hi;
lo = x.lo & y.lo;
return hi*Math.pow(2,32) + lo;
var hi = (x.hi & y.hi) >>> 0;
var lo = (x.lo & y.lo) >>> 0;
return hi*BIT32_LIMIT + lo;
}

function or64(x,y){
x = hi_lo(x);
y = hi_lo(y);
hi = x.hi | y.hi;
lo = x.lo | y.lo;
return hi*Math.pow(2,32) + lo;
var hi = (x.hi | y.hi) >>> 0;
var lo = (x.lo | y.lo) >>> 0;
return hi*BIT32_LIMIT + lo;
}

function shiftleft64(x,y){
x = hi_lo(x);
hi = x.hi * Math.pow(2,y);
lo = x.lo * Math.pow(2,y);
return hi*Math.pow(2,32) + lo;
return (x * Math.pow(2,y)) % BIT64_LIMIT; // mod64 as a safeguard against 'val' exceeding 64bit integer range
}

function shiftright64(x,y){
x = hi_lo(x);
hi = Math.floor(x.hi / Math.pow(2,y));
aux = x.hi % Math.pow(2,y);
lo = Math.floor(x.lo / Math.pow(2,y)) + aux * Math.pow(2,32-y);
return hi*Math.pow(2,32) + lo;
}
return Math.floor(x / Math.pow(2,y));
}
</script>

<script type="text/javascript">
// some simple tests for bitwise ops
// run it in the browser console for example by:
// runTests(Tests);

var Tests = {};

Tests.hi_lo = {
low: () => {
var hl = hi_lo(1078);
if(bin64(hl.hi) != '0000000000000000000000000000000000000000000000000000000000000000') return "failed: hi";
if(bin64(hl.lo) != '0000000000000000000000000000000000000000000000000000010000110110') return "failed: lo";
return true;
},
high: () => {
var hl = hi_lo(17179869182);
if(bin64(hl.hi) != '0000000000000000000000000000000000000000000000000000000000000011') return "failed: hi";
if(bin64(hl.lo) != '0000000000000000000000000000000011111111111111111111111111111110') return "failed: lo";
return true;
},
};

Tests.and64 = {
low: () => {
// 2nd arg: 32bitmask, all '1', bitvalue shouldn't change
var val = and64(1078,4294967295);
if(bin64(val) != '0000000000000000000000000000000000000000000000000000010000110110') return "failed";
return true;
},
high: () => {
// 2nd arg: 36bitmask, all '1', bitvalue shouldn't change
var val = and64(17179869182,4294967295*16+15);
if(bin64(val) != '0000000000000000000000000000001111111111111111111111111111111110') return "failed";
return true;
},
};

Tests.or64 = {
low: () => {
// 2nd arg: all '0', bitvalue shouldn't change
var val = or64(1078,0);
if(bin64(val) != '0000000000000000000000000000000000000000000000000000010000110110') return "failed";
return true;
},
high: () => {
// 2nd arg: all '0', bitvalue shouldn't change
var val = or64(17179869182,0);
if(bin64(val) != '0000000000000000000000000000001111111111111111111111111111111110') return "failed";
return true;
},
};

Tests.shiftleft64 = {
low: () => {
var val = shiftleft64(1078,16);
if(bin64(val) != '0000000000000000000000000000000000000100001101100000000000000000') return "failed";
return true;
},
low_almostlastbit: () => {
var val = shiftleft64(1078,52);
if(bin64(val) != '0100001101100000000000000000000000000000000000000000000000000000') return "failed";
return true;
},
low_lastbit: () => {
var val = shiftleft64(1078,53);
if(bin64(val) != '1000011011000000000000000000000000000000000000000000000000000000') return "failed";
return true;
},
low_overflow: () => {
var val = shiftleft64(1078,54);
if(bin64(val) != '0000110110000000000000000000000000000000000000000000000000000000') return "failed";
return true;
},

high: () => {
var val = shiftleft64(17179869182,16);
if(bin64(val) != '0000000000000011111111111111111111111111111111100000000000000000') return "failed";
return true;
},
high_almostlastbit: () => {
var val = shiftleft64(17179869182,29);
if(bin64(val) != '0111111111111111111111111111111111000000000000000000000000000000') return "failed";
return true;
},
high_lastbit: () => {
var val = shiftleft64(17179869182,30);
if(bin64(val) != '1111111111111111111111111111111110000000000000000000000000000000') return "failed";
return true;
},
high_overflow: () => {
var val = shiftleft64(17179869182,31);
if(bin64(val) != '1111111111111111111111111111111100000000000000000000000000000000') return "failed";
return true;
},
};

Tests.shiftright64 = {
// shiftright64 should be inverse of shiftleft64, except for cases when shiftleft64 overflows
low: () => {
var val = shiftright64(parseInt('0000000000000000000000000000000000000100001101100000000000000000',2),16);
if(val != 1078) return "failed";
return true;
},
low_almostlastbit: () => {
var val = shiftright64(parseInt('0100001101100000000000000000000000000000000000000000000000000000',2),52);
if(val != 1078) return "failed";
return true;
},
// in our case, shiftright64 should never return negatives,
// even if MSB of the input is set and input value could look like a negative
low_lastbit: () => {
var val = shiftright64(parseInt('1000011011000000000000000000000000000000000000000000000000000000',2),53);
if(val != 1078) return "failed";
return true;
},

high: () => {
var val = shiftright64(parseInt('0000000000000011111111111111111111111111111111100000000000000000',2),16);
if(val != 17179869182) return "failed";
return true;
},
high_almostlastbit: () => {
var val = shiftright64(parseInt('0111111111111111111111111111111111000000000000000000000000000000',2),29);
if(val != 17179869182) return "failed";
return true;
},
// in our case, shiftright64 should never return negatives,
// even if MSB of the input is set and input value could look like a negative
high_lastbit: () => {
var val = shiftright64(parseInt('1111111111111111111111111111111110000000000000000000000000000000',2),30);
if(val != 17179869182) return "failed";
return true;
},
};

/*
results before patch, original code gitrev:514d9bb6ed6c9a07c4e595947ab71fe424a49845
On: Google Chrome 69.0.3497.100, Windows 10 x64
and64: high: failed
or64: high: failed
shiftleft64: low: failed
shiftleft64: low_overflow: failed
shiftleft64: high_overflow: failed
shiftright64: low: failed
Failed 6/20 tests
*/

// FYI: doing that this way will have funny effects if 'n' is negative (i.e. -7 = '0000000000-111')
// but we don't care - 'n' should be positive and any funny effects will fail the test so it's OK
function bin64(n) {
return n.toString(2).padStart(64,'0');
}

// right now, I don't want to make fuss and install Jasmine/etc, so here's my quick'n'dirty test runner
function runTests(suite) {
var testlist = Object.keys(Tests).map(k => Object.keys(Tests[k]).map(k2 => ({outer:k,inner:k2})));
testlist = testlist.flat();
var results = testlist.map(what => {
var result = suite[what.outer][what.inner]();
if(result === true) return [true, what.outer, what.inner, null];
console.log(what.outer + ": " + what.inner + ": " + result);
return [false, what.outer, what.inner, result];
});
var countfailed = results.filter(arr => !arr[0]).length;
if(countfailed > 0) {
console.log("Failed " + countfailed + "/" + results.length + " tests");
return false;
}
else
return true;
}

</script>
</body>
</html>