@@ -328,6 +328,18 @@ function convertToInt(
328328 upperBound = pow2 ( bitLength - 1 ) - 1 ;
329329 }
330330
331+ // Common case: primitive Number values that already fit the Web IDL
332+ // range and have no fractional part are returned unchanged by every
333+ // ConvertToInt path, except that -0 must become +0. This skips the
334+ // generic ToNumber and option handling without skipping observable
335+ // object coercion.
336+ if ( typeof V === 'number' &&
337+ V >= lowerBound &&
338+ V <= upperBound &&
339+ MathTrunc ( V ) === V ) {
340+ return V === 0 ? 0 : V ;
341+ }
342+
331343 // Step 4: convert V with ECMA-262 ToNumber.
332344 let x = toNumber ( V , options ) ;
333345 // Step 5: normalize -0 to +0.
@@ -373,6 +385,14 @@ function convertToInt(
373385 // Step 9: truncate to IntegerPart(x).
374386 x = integerPart ( x ) ;
375387
388+ // Steps 10-12 are an identity for values already in the step 1-3
389+ // bounds. For 64-bit conversions this only skips the safe-integer
390+ // subset; values outside it still need exact BigInt modulo and the
391+ // final Number approximation.
392+ if ( x >= lowerBound && x <= upperBound ) {
393+ return x ;
394+ }
395+
376396 if ( bitLength === 64 ) {
377397 // Steps 10-12 still wrap over the full 2^64 IDL integer range.
378398 // BigInt keeps x modulo 2^64 and the signed high-bit adjustment exact
@@ -396,11 +416,13 @@ function convertToInt(
396416 }
397417
398418 // Step 10: reduce modulo 2^bitLength.
399- x = modulo ( x , pow2 ( bitLength ) ) ;
419+ const twoToTheBitLength = pow2 ( bitLength ) ;
420+ const twoToOneLessThanTheBitLength = pow2 ( bitLength - 1 ) ;
421+ x = modulo ( x , twoToTheBitLength ) ;
400422
401423 // Step 11: wrap into the signed range when the high bit is set.
402- if ( signed && x >= pow2 ( bitLength - 1 ) ) {
403- return x - pow2 ( bitLength ) ;
424+ if ( signed && x >= twoToOneLessThanTheBitLength ) {
425+ return x - twoToTheBitLength ;
404426 }
405427
406428 // Step 12: return the unsigned value.
0 commit comments