JavaScript 能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个整数。

    MDN Number
    需要注意的是,由于整数的范围限制,如果计算结果超出了范围就会产生溢出。产生溢出时运行不会出错,但结果可能会出乎意料。如果除数为0,那么整数的除法在运行时将报错。

    题目1:整数除法

    题目:输入2个int型整数,它们进行除法计算并返回商,要求不得使用乘号’*’、除号’/‘及求余符号’%’。当发生溢出时,返回最大的整数值。假设除数不为0。例如,输入15和2,输出15/2的结果,即7。

    分析:这个题目限制我们不能使用乘号和除号进行运算。一个直观的解法是基于减法实现除法。例如,为了求得15/2的商,可以不断地从15里减去2,当减去7个2之后余数是1,此时不能再减去更多的2,因此15/2的商是7。我们可以用一个循环实现这个过程。

    但这个直观的解法存在一个问题。当被除数很大但除数很小时,减法操作执行的次数会很多。例如,求(231-1)/1,减1的操作将执行232-1次,需要很长的时间。如果被除数是n,那么这种解法的时间复杂度为O(n)。我们需要对这种解法进行优化。

    可以将上述解法稍做调整。当被除数大于除数时,继续比较判断被除数是否大于除数的2倍,如果是,则继续判断被除数是否大于除数的4倍、8倍等。如果被除数最多大于除数的2k倍,那么将被除数减去除数的2k倍,然后将剩余的被除数重复前面的步骤。由于每次将除数翻倍,因此优化后的时间复杂度是O(logn)。

    下面以15/2为例讨论计算的过程。15大于2,也大于2的2倍(即4),还大于2的4倍(即8),但小于2的8倍(即16)。于是先将15减去8,还剩余7。由于减去的是除数的4倍,减去这部分对应的商是4。接下来对剩余的7和除数2进行比较,7大于2,大于2的2倍(即4),但小于2的4倍(即8),于是将7减去4,还剩余3。这一次减去的是除数2的2倍,对应的商是2。然后对剩余的3和除数2进行比较,3大于2,但小于2的2倍(即4),于是将3减去2,还剩余1。这一次减去的是除数的1倍,对应的商是1。最后剩余的数字是1,比除数小,不能再减去除数了。于是15/2的商是4+2+1,即7。

    上述讨论假设被除数和除数都是正整数。如果有负数则可以将它们先转换成正数,计算正数的除法之后再根据需要调整商的正负号。例如,如果计算-15/2,则可以先计算15/2,得到的商是7。由于被除数和除数中有一个负数,因此商应该是负数,于是商应该是-7。

    将负数转换成正数存在一个小问题。对于32位的整数而言,最小的整数是-231,最大的整数是231-1。因此,如果将-231转换为正数则会导致溢出。由于将任意正数转换为负数都不会溢出,因此可以先将正数都转换成负数,用前面优化之后的减法计算两个负数的除法,然后根据需要调整商的正负号。

    最后讨论可能的溢出。由于是整数的除法并且除数不等于0,因此商的绝对值一定小于或等于被除数的绝对值。因此,int型整数的除法只有一种情况会导致溢出,即(-231)/(-1)。这是因为最大的正数为231-1,231超出了正数的范围。

    1. /**
    2. * @param {number} a
    3. * @param {number} b
    4. * @return {number}
    5. */
    6. // 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2^31−1]。本题中,如果除法结果溢出,则返回 231 − 1
    7. // -0x80000000为最小的int型整数,即-2^31,0xc0000000是它的一半,即-2^30。
    8. var divide = function (a, b) {
    9. // 0x7fffffff就是2^31 - 1
    10. if (a === -0x80000000 && b === -1) {
    11. return 0x7fffffff;
    12. }
    13. let negative = 2;
    14. if (a > 0) {
    15. negative--;
    16. a = -a;
    17. }
    18. if (b > 0) {
    19. negative--;
    20. b = -b;
    21. }
    22. let res = divideCore(a, b);
    23. return negative == 1 ? -res : res
    24. };
    25. function divideCore(a, b) {
    26. let ret = 0;
    27. // 注意a, b都是负数,所以a <= b就是还可以继续除
    28. debugger
    29. while (a <= b) {
    30. let value = b;
    31. let quo = 1;
    32. let beforeValue = b;
    33. let beforeQuo = 1;
    34. while (value >= -0xc0000000 && a <= value) {
    35. beforeValue = value;
    36. beforeQuo = quo;
    37. quo += quo; // 几倍的value,即几倍的b
    38. value += value; // 这里-0xc0000000 + -0xc0000000在32 位有符号整数中就越界了
    39. // console.log(beforeValue,value)
    40. }
    41. // a: -15
    42. // b: -2 -4 -8 -16
    43. // quo: 1 2 4 8
    44. value = value - beforeValue;
    45. quo = quo - beforeQuo;
    46. // console.log(value)
    47. // console.log(quo)
    48. ret += quo;
    49. a -= value;
    50. }
    51. return ret;
    52. }
    1. /**
    2. * @param {number} a
    3. * @param {number} b
    4. * @return {number}
    5. */
    6. // 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−2^31, 2^31−1]。本题中,如果除法结果溢出,则返回 231 − 1
    7. // -0x80000000为最小的int型整数,即-2^31,0xc0000000是它的一半,即-2^30。
    8. var divide = function (a, b) {
    9. // 0x7fffffff就是2^31 - 1
    10. if (a === -0x80000000 && b === -1) {
    11. return 0x7fffffff;
    12. }
    13. let negative = 2;
    14. if (a > 0) {
    15. negative--;
    16. a = -a;
    17. }
    18. if (b > 0) {
    19. negative--;
    20. b = -b;
    21. }
    22. let res = divideCore(a, b);
    23. return negative == 1 ? -res : res
    24. };
    25. function divideCore(a, b) {
    26. let ret = 0;
    27. // 注意a, b都是负数,所以a <= b就是还可以继续除
    28. debugger
    29. while (a <= b) {
    30. let value = b;
    31. let quo = 1;
    32. while (value >= -0xc0000000 && a <= value + value) {
    33. beforeValue = value;
    34. beforeQuo = quo;
    35. quo += quo; // 几倍的value,即几倍的b
    36. value += value; // 这里-0xc0000000 + -0xc0000000在32 位有符号整数中就越界了
    37. // console.log(beforeValue,value)
    38. }
    39. // a: -15
    40. // b: -2 -4 -8 -16
    41. // quo: 1 2 4 8
    42. // console.log(value)
    43. // console.log(quo)
    44. ret += quo;
    45. a -= value;
    46. }
    47. return ret;
    48. }

    题目地址