Added Int36 overflow/underflow detection
This commit is contained in:
parent
798fae1898
commit
40311e82b2
1 changed files with 35 additions and 18 deletions
|
|
@ -38,7 +38,7 @@ var DEBUG = true;
|
||||||
* @property {number} error
|
* @property {number} error
|
||||||
*
|
*
|
||||||
* The 'value' property stores the 36-bit value as an unsigned integer. When the value
|
* The 'value' property stores the 36-bit value as an unsigned integer. When the value
|
||||||
* should be interpreted as a signed quantity, subtract BIT36 whenever value > MAXSIGNED.
|
* should be interpreted as a signed quantity, subtract BIT36 whenever value > MAXPOS.
|
||||||
*
|
*
|
||||||
* The 'extended' property stores an additional 36 bits of data from a multiplication;
|
* The 'extended' property stores an additional 36 bits of data from a multiplication;
|
||||||
* it must also be set prior to a division. Internally, it will be set to null whenever the
|
* it must also be set prior to a division. Internally, it will be set to null whenever the
|
||||||
|
|
@ -65,7 +65,7 @@ class Int36 {
|
||||||
* 0 <= i <= Math.pow(2, 36) - 1
|
* 0 <= i <= Math.pow(2, 36) - 1
|
||||||
*
|
*
|
||||||
* The lower bound is ZERO and the upper bound is Int36.MAXVAL. Whenever an Int36 value should be
|
* The lower bound is ZERO and the upper bound is Int36.MAXVAL. Whenever an Int36 value should be
|
||||||
* interpreted as a signed value, values above Int36.MAXSIGNED (ie, values with bit 35 set) should be
|
* interpreted as a signed value, values above Int36.MAXPOS (ie, values with bit 35 set) should be
|
||||||
* converted to their signed counterpart by subtracting the value from Int36.BIT36 (aka MAXVAL + 1);
|
* converted to their signed counterpart by subtracting the value from Int36.BIT36 (aka MAXVAL + 1);
|
||||||
* the easiest way to do that is call isNegative() to check the sign bit and then call negate() as
|
* the easiest way to do that is call isNegative() to check the sign bit and then call negate() as
|
||||||
* appropriate.
|
* appropriate.
|
||||||
|
|
@ -204,7 +204,14 @@ class Int36 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* truncate(result)
|
* truncate(result, original)
|
||||||
|
*
|
||||||
|
* The range of valid results (0 - MAXVAL) is divided into two equal sub-ranges: 0 to MAXPOS,
|
||||||
|
* where the sign bit is zero (the bottom range), and MAXPOS+1 to MAXVAL (the top range), where
|
||||||
|
* the sign bit is one. During a single arithmetic operation, the result can "wrap around" from
|
||||||
|
* the bottom range to the top, or from the top range to the bottom, but it's an overflow/underflow
|
||||||
|
* condition ONLY if the result "wraps across" the midpoint between the two ranges, producing an
|
||||||
|
* unnaturally small delta (<= MAXPOS).
|
||||||
*
|
*
|
||||||
* NOTE: This function's job is to truncate the result of an operation to 36-bit accuracy,
|
* NOTE: This function's job is to truncate the result of an operation to 36-bit accuracy,
|
||||||
* not to remove any fractional portion that might also exist. If an operation could have produced
|
* not to remove any fractional portion that might also exist. If an operation could have produced
|
||||||
|
|
@ -212,13 +219,15 @@ class Int36 {
|
||||||
*
|
*
|
||||||
* @this {Int36}
|
* @this {Int36}
|
||||||
* @param {number} result
|
* @param {number} result
|
||||||
|
* @param {number} [original]
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
truncate(result)
|
truncate(result, original)
|
||||||
{
|
{
|
||||||
if (DEBUG && result !== Math.trunc(result)) {
|
if (DEBUG && result !== Math.trunc(result)) {
|
||||||
console.log("Int36.truncate(" + result + " is not an integer)");
|
console.log("Int36.truncate(" + result + " is not an integer)");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.extended = null;
|
this.extended = null;
|
||||||
this.remainder = null;
|
this.remainder = null;
|
||||||
this.error = Int36.ERROR.NONE;
|
this.error = Int36.ERROR.NONE;
|
||||||
|
|
@ -226,7 +235,15 @@ class Int36 {
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
result += Int36.BIT36;
|
result += Int36.BIT36;
|
||||||
}
|
}
|
||||||
|
|
||||||
result %= Int36.BIT36;
|
result %= Int36.BIT36;
|
||||||
|
|
||||||
|
if (original !== undefined && (result > Int36.MAXPOS) != (original > Int36.MAXPOS)) {
|
||||||
|
var delta = result - original;
|
||||||
|
if (Math.abs(delta) <= Int36.MAXPOS) {
|
||||||
|
this.error |= (delta > 0? Int36.ERROR.OVERFLOW : Int36.ERROR.UNDERFLOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,7 +255,7 @@ class Int36 {
|
||||||
*/
|
*/
|
||||||
add(i36)
|
add(i36)
|
||||||
{
|
{
|
||||||
this.value = this.truncate(this.value + i36.value);
|
this.value = this.truncate(this.value + i36.value, this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -249,7 +266,7 @@ class Int36 {
|
||||||
*/
|
*/
|
||||||
addNum(num)
|
addNum(num)
|
||||||
{
|
{
|
||||||
this.value = this.truncate(this.value + Int36.validate(num));
|
this.value = this.truncate(this.value + Int36.validate(num), this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -260,7 +277,7 @@ class Int36 {
|
||||||
*/
|
*/
|
||||||
sub(i36)
|
sub(i36)
|
||||||
{
|
{
|
||||||
this.value = this.truncate(this.value - i36.value);
|
this.value = this.truncate(this.value - i36.value, this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -271,7 +288,7 @@ class Int36 {
|
||||||
*/
|
*/
|
||||||
subNum(num)
|
subNum(num)
|
||||||
{
|
{
|
||||||
this.value = this.truncate(this.value - Int36.validate(num));
|
this.value = this.truncate(this.value - Int36.validate(num), this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -312,12 +329,12 @@ class Int36 {
|
||||||
var fNeg = false, extended;
|
var fNeg = false, extended;
|
||||||
var n1 = this.value, n2 = value;
|
var n1 = this.value, n2 = value;
|
||||||
|
|
||||||
if (n1 > Int36.MAXSIGNED) {
|
if (n1 > Int36.MAXPOS) {
|
||||||
n1 = Int36.BIT36 - n1;
|
n1 = Int36.BIT36 - n1;
|
||||||
fNeg = !fNeg;
|
fNeg = !fNeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n2 > Int36.MAXSIGNED) {
|
if (n2 > Int36.MAXPOS) {
|
||||||
n2 = Int36.BIT36 - n2;
|
n2 = Int36.BIT36 - n2;
|
||||||
fNeg = !fNeg;
|
fNeg = !fNeg;
|
||||||
}
|
}
|
||||||
|
|
@ -391,7 +408,7 @@ class Int36 {
|
||||||
*/
|
*/
|
||||||
var bNegLo = 0, bNegHi = 0;
|
var bNegLo = 0, bNegHi = 0;
|
||||||
|
|
||||||
if (divisor > Int36.MAXSIGNED) {
|
if (divisor > Int36.MAXPOS) {
|
||||||
divisor = Int36.BIT36 - divisor;
|
divisor = Int36.BIT36 - divisor;
|
||||||
bNegLo = 1 - bNegLo;
|
bNegLo = 1 - bNegLo;
|
||||||
}
|
}
|
||||||
|
|
@ -437,10 +454,10 @@ class Int36 {
|
||||||
this.extended = null;
|
this.extended = null;
|
||||||
this.remainder = bitsRem[0];
|
this.remainder = bitsRem[0];
|
||||||
|
|
||||||
if (bNegLo && this.value && this.value > Int36.MINSIGNED) {
|
if (bNegLo && this.value) {
|
||||||
this.value = Int36.BIT36 - this.value;
|
this.value = Int36.BIT36 - this.value;
|
||||||
}
|
}
|
||||||
if (bNegHi && this.remainder && this.remainder > Int36.MINSIGNED) {
|
if (bNegHi && this.remainder) {
|
||||||
this.remainder = Int36.BIT36 - this.remainder;
|
this.remainder = Int36.BIT36 - this.remainder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -454,7 +471,7 @@ class Int36 {
|
||||||
*/
|
*/
|
||||||
isNegative()
|
isNegative()
|
||||||
{
|
{
|
||||||
return (this.extended > Int36.MAXSIGNED || this.extended == null && this.value > Int36.MAXSIGNED);
|
return (this.extended > Int36.MAXPOS || this.extended == null && this.value > Int36.MAXPOS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -466,7 +483,7 @@ class Int36 {
|
||||||
/*
|
/*
|
||||||
* Set extended to match the sign of the (negated) value.
|
* Set extended to match the sign of the (negated) value.
|
||||||
*/
|
*/
|
||||||
this.extended = (this.value > Int36.MAXSIGNED? 0 : Int36.MAXVAL);
|
this.extended = (this.value > Int36.MAXPOS? 0 : Int36.MAXVAL);
|
||||||
}
|
}
|
||||||
else if (this.value) {
|
else if (this.value) {
|
||||||
/*
|
/*
|
||||||
|
|
@ -590,7 +607,7 @@ class Int36 {
|
||||||
*/
|
*/
|
||||||
static validate(num)
|
static validate(num)
|
||||||
{
|
{
|
||||||
if (num < 0 && num >= Int36.MINSIGNED) {
|
if (num < 0 && num >= Int36.MINNEG) {
|
||||||
num += Int36.BIT36;
|
num += Int36.BIT36;
|
||||||
}
|
}
|
||||||
var value = Math.trunc(Math.abs(num)) % Int36.BIT36;
|
var value = Math.trunc(Math.abs(num)) % Int36.BIT36;
|
||||||
|
|
@ -611,8 +628,8 @@ Int36.ERROR = {
|
||||||
Int36.BIT18 = Math.pow(2, 18); // 262,144
|
Int36.BIT18 = Math.pow(2, 18); // 262,144
|
||||||
Int36.BIT36 = Math.pow(2, 36); // 68,719,476,736
|
Int36.BIT36 = Math.pow(2, 36); // 68,719,476,736
|
||||||
|
|
||||||
Int36.MAXSIGNED = Math.pow(2, 35) - 1; // 34,359,738,367
|
Int36.MAXPOS = Math.pow(2, 35) - 1; // 34,359,738,367
|
||||||
Int36.MINSIGNED = -Math.pow(2, 35); // -34,359,738,368
|
Int36.MINNEG = -Math.pow(2, 35); // -34,359,738,368
|
||||||
|
|
||||||
Int36.MAXVAL = Math.pow(2, 36) - 1; // 68,719,476,735
|
Int36.MAXVAL = Math.pow(2, 36) - 1; // 68,719,476,735
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue