Does Javascript handle integer overflow and underflow? If yes, how? - javascript

We know that Java does not handle underflows and overflows, but how does Javascript handle these for integers?
Does it go back to a minimum/maximum? If yes, which minimum/maximum?
I need to split a string and compute a hash value based on its characters.

In a simple test, when I try this:
var max = Number.MAX_VALUE;
var x = max + 10;
var min = Number.MIN_VALUE;
var y = min / 10;
I find that x and max have the same value (in Chrome, IE and Firefox) so it appears that some overflows are just pegged to the max value. And, y gets pegged to 0 so some underflows seem to go to zero.
Ahhh, but it is not quite that simple. Not all overflows go to Number.MAX_VALUE and not all underflows go to Number.MIN_VALUE. If you do this:
var max = Number.MAX_VALUE;
var z = max * 2;
Then, z will be Infinity.
It turns out that it depends upon how far you overflow/underflow. If you go too far, you will get INFINITY instead. This is because of the use of IEEE 754 round-to-nearest mode where the max value can be considered nearer than infinity. See Adding to Number.MAX_VALUE for more detail. Per that answer, values of 1.7976931348623158 × 10308 or greater round to infinity. Values between Number.MAX_VALUE and that will round to Number.MAX_VALUE.
To, make things even more complicated, there is also something as gradual underflow which Javascript supports. This is where the mantissa of the floating point value has leading zeroes in it. Gradual underflow allows floating point to represent some smaller numbers that it could not represent without that, but they are represented at a reduced precision.
You can see exactly where the limits are:
>>> Number.MAX_VALUE + 9.979201e291
1.7976931348623157e+308
>>> Number.MAX_VALUE + 9.979202e291
Infinity
Here's a runnable snippet you can try in any browser:
var max = Number.MAX_VALUE;
var x = max + 10;
var min = Number.MIN_VALUE;
var y = min / 10;
var z = max * 2;
document.getElementById("max").innerHTML = max;
document.getElementById("max10").innerHTML = x;
document.getElementById("min").innerHTML = min;
document.getElementById("min10").innerHTML = y;
document.getElementById("times2").innerHTML = z;
body {
font-family: "Courier New";
white-space:nowrap;
}
Number.MAX_VALUE = <span id="max"></span><br>
Number.MAX_VALUE + 10 = <span id="max10"></span><br>
<br>
Number.MIN_VALUE = <span id="min"></span><br>
Number.MIN_VALUE / 10 = <span id="min10"></span><br>
<br>
Number.MAX_VALUE * 2 = <span id="times2"></span><br>

The maximum and minimum is +/- 9007199254740992
Try these Number type properties:
alert([Number.MAX_VALUE, Number.MIN_VALUE]);
From the ECMAScript 2020 language specification, section "The Number Type":
Note that all the positive and negative mathematical integers whose magnitude is no
greater than 253 are representable in the Number type (indeed, the
mathematical integer 0 has two representations, +0 and −0).
Test:
var x = 9007199254740992;
var y = -x;
x == x + 1; // true !
y == y - 1; // also true !

Number
In JavaScript, number type is 64 bit IEEE 754 floating point number which is not an integer. So it don't follow common patterns of integer overflow / underflow behavior in other languages.
As the floating point number use 53 bits for base part. It may represent numbers in range Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER (-253+1 to 253-1) without floating point errors. For numbers out of this range, it may be rounded to nearest number available, or may be Infinity if it is too large.
Bit-wise operator
Bit-wise operator treat operand 32 bit integers. And common integer overflow may happened as in other languages. Only last 32 bits may be kept after calculate. For example, 3<<31 would results -2147483648.
>>> treat operand as unsigned 32 bit integers. All other operator treat operand as signed 32 bit integers. If you want to convert signed integer to unsigned, you may write value >>> 0 to do the trick. To convert back, use value | 0.
If you want to shift an integer with 33, it will actually be shifted with 1.
BigInt
Just like Java's java.math.BigInteger, BigInt supports unbounded integers (still bound by your memory limit though). So integer overflow may never happen here.
TypedArray
For most TypedArray types, when an integer out of supported range assigned, it got truncated as what other languages do when converting integers, by keeping least significant bits. For example new Int8Array([1000])[0] got -24.
Uint8ClampedArray is a bit different from other TypedArray's. Uint8ClampedArray supports integers in range 0 ~ 255. When numbers out of range is used, 0 or 255 will be set instead.
asm.js
The same rules for bit-wise operator applied here. The value would be trucked back as what | 0 or >>> 0 do.

Related

Calling parseInt with a string which represents a value larger than Number.MAX_SAFE_INTEGER

I would like to ask about the behavior of parseInt when called with a string which represents an integer value larger than Number.MAX_SAFE_INTEGER.
Technically, I assume I should be expecting the same outcome which I would get by using that value directly (i.e., as a number rather than as a string).
For example, the following two will yield the same value (whether its accurate or not):
const x = parseInt("0x100000000000000000000000000000000");
const x = 0x100000000000000000000000000000000;
I do understand, however, that perhaps JS doesn't guarantee this.
So what I would really like to know is whether I can at least count on parseInt to return a value different than 0, when called with a string which represents an integer value larger than Number.MAX_SAFE_INTEGER.
The problem is fundamental with number representations - any numeric that is over Number.MAX_SAFE_INTEGER is not safe for use any more. Basic example:
const max = Number.MAX_SAFE_INTEGER;
const maxPlus1 = Number.MAX_SAFE_INTEGER + 1;
const maxPlus2 = Number.MAX_SAFE_INTEGER + 2;
console.log("max:", max); //9007199254740991
console.log("max + 1:", maxPlus1); //9007199254740992
console.log("max + 2:", maxPlus2); //9007199254740992
console.log("max + 1 = max + 2:", maxPlus1 === maxPlus2); //true
As you can see, almost immediately after you breach the Number.MAX_SAFE_INTEGER barrier, you run into problems with precision. JavaScript uses the IEEE 754 standard to represents numbers and while it can show numbers than its highest (as opposed to, say an int field in another language which will overflow to zero or maximum negative), such representations are imprecise. Some big numbers cannot be represented like 9007199254740993 (which is Number.MAX_SAFE_INTEGER + 2) and you get a different number instead.
The exact same thing applies to parseInt, since it will convert the string into a JavaScript numeric, there might not be a precise representation for it:
const maxPlus1String = "9007199254740992";
const maxPlus2String = "9007199254740993";
const maxPlus1 = parseInt(maxPlus1String);
const maxPlus2 = parseInt(maxPlus2String);
console.log("max + 1:", maxPlus1); //9007199254740992
console.log("max + 2:", maxPlus2); //9007199254740992
console.log("(string) max + 1 = max + 2:", maxPlus1String === maxPlus2String); //false
console.log("max + 1 = max + 2:", maxPlus1 === maxPlus2); //true
Ultimately, it's a question of how floating point numbers are represented. Wikipedia has a good article but I'll simplify it to the most important parts:
With floating point representation, you keep a mantissa (also called significand with d at the end) and exponent for each number. This works like scientific notation, so I'll use that for easier referencing:
1.23e5 = 1.23 * 105 = 123 000
1.23 is the mantissa of the number.
5 is the exponent.
With both of these, you can represent numbers arbitrarily high in very short form. However, with floating point representation, you are limited to how many bits of each you can keep. This comes at the cost of sacrificing accuracy once you run out of numbers for the mantissa, you lose precision. So, if we decide to only allow one decimal place in our scientific notation, we get the number 1.2e5 which could be 123 000 but it might also be 120 000 or 125 000 or 128 215 - we cannot recreate it from the shortened form. Similar thing happens with floating point numbers - once you don't have enough digits for the mantissa, the rest are discarded, so you don't get the exact number back.
When the exponent runs out of digits you reach the highest number representable.
In JavaScript, the maximum number possible can be seen in Number.MAX_VALUE:
console.log(Number.MAX_VALUE)
1.7976931348623157e+308 is pretty large, with an exponent of 308. So you can represent a lot of numbers with this and if you use parseInt anything under this value, you will get some number that is in the region of what you parsed.
However, what happens if you go over? Well, you'll reach a value within the number range that is representable in JavaScript which is reserved for a special reason. This is the absolute top most number - a floating point representation for the largest number possible to be represented. That value is Infinity. If you happen to parse something that is larger than Number.MAX_VALUE, you'll get Infinity instead:
const largeNum = "17" + "0".repeat(307); //1.7e308
const tooLargeNum = "18" + "0".repeat(307); //1.8e308
console.log("large number string:", largeNum);
console.log("large number parsed:", parseInt(largeNum));
console.log("too large number string:", tooLargeNum);
console.log("too large number parsed:", parseInt(tooLargeNum));
So, even if you have astronomically large numbers, you are guaranteed to have a number more than zero, since Infinity > 0

Indexing an array by a quotient

In the following code, i will always be an even number so the quotient i / 2 should always be an integer. Should I still use Math.floor(i / 2) to be on the safe side? I'm asking because JavaScript does treat all numbers as floating points so I'm concerned about rounding errors.
for (var i = 0; i < data.length; i = i + 2) {
var name = names[i / 2];
...
}
No. you do not have to use Math.floor() in this situation.
Because i is always even and also names[1.00] is equivalent to names[1].
To check, try the below in a javascript console.
The length of array will be 20 and the first 10 array items will be printed
var names = ["nums1", "nums2", "nums3","nums4", "nums5", "nums6","nums7",
"nums8", "nums9","nums10", "nums11","nums12", "nums13","nums14", "nums15",
"nums16","nums17", "nums18", "nums19","nums20"];
for (var i = 0; i < names.length; i = i + 2) {
console.log(names[i/2]);
}
Dividing even integers by 2 always returns an integer as long as it does not overflow
Number.MAX_SAFE_INTEGER
after that, integers are stored with a power of two, so they lose precision.
However arrays which are bigger than this size would have problems accessing their elements (as indexes get imprecise), the maximum number of elements you can push into an array is limited to exactly this maximum safe integer. So basically your code will always work, if it doesn't, that's because the array overflows and not because the index is wrong. However, I rather recommend you to do:
var array = ["one", "two", "three","four", "five", "six","seven",
"eight", "nine","ten", "eleven","twelve", "thirteen","fourteen", "fifteen",
"sixteen","seventeen", "eighteen", "nineteen","twenty"];
for(var i=0,l=array.length/2;i<l;i++){
console.log(array[i]);
}
As it saves these unnecessary math operations...
According to the JavaScript(ECMAScript®) Language Specification if
i <= Number.MAX_SAFE_INTEGER + 1
then i can be exactly represented and the division should work out fine.
The value of Number.MAX_SAFE_INTEGER is the largest integer n such that n and n + 1 are both exactly representable as a Number value.
The value of Number.MAX_SAFE_INTEGER is 9007199254740991 (253−1).
Section 12.7.3.2 Applying the / Operator
In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, the quotient is computed and rounded to the nearest representable value using IEEE 754-2008 round to nearest, ties to even mode. If the magnitude is too large to represent, the operation overflows; the result is then an infinity of appropriate sign. If the magnitude is too small to represent, the operation underflows and the result is a zero of the appropriate sign. The ECMAScript language requires support of gradual underflow as defined by IEEE 754-2008.

Javascript precision while dividing

Is there a way to determine whether dividing one number by another will result in whole number in JavaScript? Like 18.4 / 0.002 gives us 9200, but 18.4 / 0.1 gives us 183.99999999999997. The problem is that both of them may be any float number (like 0.1, 0.01, 1, 10, ...) which makes it impossible to use the standard function modulo or trying to subtract, and floating point precision issues mean we will sometimes get non-whole-number results for numbers that should be whole, or whole-number results for ones that shouldn't be.
One hacky way would be
Convert both numbers to strings with toString()
Count the precision points (N) by stripping off the characters before the . (including the .) and taking the length of the remaining part
Multiply with 10^N to make them integers
Do modulo and get the result
Updated Demo: http://jsfiddle.net/9HLxe/1/
function isDivisible(u, d) {
var numD = Math.max(u.toString().replace(/^\d+\./, '').length,
d.toString().replace(/^\d+\./, '').length);
u = Math.round(u * Math.pow(10, numD));
d = Math.round(d * Math.pow(10, numD));
return (u % d) === 0;
}
I don't think you can do that with JavaScript's double-precision floating point numbers, not reliably across the entire range. Maybe within some constraints you could (although precision errors crop up in all sorts of -- to me -- unexpected locations).
The only way I see is to use any of the several "big decimal" libraries for JavaScript, that don't use Number at all. They're slower, but...
I Assume that you want the reminder to be zero when you perform the division.
check for the precision of the divisor, and multiply both divisor and divident by powers of 10
for example
you want to check for 2.14/1.245 multiply both divident and divisor by 1000 as 1.245 has 3 digits precision, now the you would have integers like 2140/1245 to perform modulo
Divide first number by second one and check if result is integer ?
Only, when you check that the result is integer, you need to specify a rounding threshold.
In javascript, 3.39/1.13 is slightly more than 3.
Example :
/**
* Returns true iif a is an integer multiple of b
*/
function isIntegerMultiple(a, b, precision) {
if (precision === undefined) {
precision = 10;
}
var quotient = a / b;
return Math.abs(quotient - Math.round(quotient)) < Math.pow(10, -precision);
}
console.log(isIntegerMultiple(2, 1)); // true
console.log(isIntegerMultiple(2.4, 1.2)); // true
console.log(isIntegerMultiple(3.39, 1.13)); // true
console.log(isIntegerMultiple(3.39, 1.13, 20)); // false
console.log(isIntegerMultiple(3, 2)); // false
Have a look at this for more details on floating point rounding issues: Is floating point math broken?

Is this a valid way to truncate a number?

I found this code in a SO answer as a way to truncate a number into an integer in Javascript:
var num = -20.536;
var result = num | 0;
//result = -20
Is this a valid way to truncate a number in Javascript, or it is some kind of hack? Why does it works only with numbers less than 2147483647?
That method works by implicitly converting the number to a 32-bit integer, as binary operators use 32-bit integers in their calculations.
The drawbacks of that method are:
The desired operation is hidden as an implicit effect of the operator, so it's not easy to see what the intention of the code is.
It can only handle integers within the range of a 32-bit number.
For any regular case you should use the Math.floor or Math.ceil methods instead, it clearly shows what the intention of the code is, and it handles any number within the precision range of a double, i.e. integers up to 52 bits:
var num = 20.536;
var result = Math.floor(num); // 20
var num = -20.536;
var result = Math.ceil(num); // -20
There is no round-towards-zero method in Javascript, so to do that you would need to check the sign before rounding:
var result = num < 0 ? Math.ceil(num) : Math.floor(num);
Use Javascript's parseInt like so:
var num = -20.536;
var num2int = parseInt(num);
return num2int; //returns -20
Tada! num is now an int with the value of -20.
If you use parseInt you can go from -2^53 to +2^53:
parseInt(-20.536) // -20
parseInt(9007199254740992.1234) // 9007199254740992
Why +/- 2^53? This is because JavaScript uses a 64-bit representation for floating point numbers, with a 52-bit mantissa. Hence all integer values up to 2^53 can be represented exactly. Beyond this, whole numbers are approximated.

What is the maximum integer allowed in a javascript variable? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is JavaScript's Max Int? What's the highest Integer value a Number can go to without losing precision?
What is the maximum integer in javascript? I have a variable that starts in 0 and adds 100 each 0.1 seconds. What is the maximum number it can reach?
BTW, I thought this question had been answered before but I couldn't find it. If it is answered, please send me a link to it =) thanks!
JavaScript numbers are IEE 794 floating point double-precision values. There's a 53-bit mantissa (from memory), so that's pretty much the limit.
Now there are times when JavaScript semantics call for numbers to be cast to a 32-bit integer value, like array indexing and bitwise operators.
A javascript variable can have any value you like. If native support isn't sufficient, there are various libraries that provide support for unlimited precision arithmetic (e.g. BigInt.js).
The largest value for the ECMAScript Number Type is +ve infinity (but infinity isn't a number, it's a concept). The largest numeric value is given by Number.MAX_VALUE, which is just the maximum value representable by an IEEE 754 64-bit double-precision number.
Some quirks:
var x = Number.MAX_VALUE;
var y = x - 1;
var z = x - 2;
x == y; // true
x == z; // false
The range of contiguous integers representable in ECMAScript is from -2^53 to +2^53. The largest exponent is 2^1023.
It is 1.7976931348623157e+308
to try it yourself code it
CODE
alert(Number.MAX_VALUE);
http://jsfiddle.net/XHcZx/

Categories

Resources