前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[每日一题] JavaScript面试之“大数相加”运算

[每日一题] JavaScript面试之“大数相加”运算

作者头像
用户1462769
发布2019-08-08 17:09:50
3.8K0
发布2019-08-08 17:09:50
举报
文章被收录于专栏:全栈者全栈者

微信公众号:全栈者 作者:TingRongGao Tips:《每日一题》栏目是作者打造的一个面试题分享专栏,旨在每日能够分享一道面试题给你,每道题精益求精,希望你能坚持学习,作者也会坚持每日更新,期待你的关注,相信量变一定会引起质变!

为什么2^53和2^53+1相等?

先给大家抛出一个问题,思考一下两个判断是否相等,然后带着答案往下阅读。

代码语言:javascript
复制
// 第一道题
Math.pow(2, 53) === Math.pow(2, 53) + 1 //是否为true?
// 第二道题
9007199254740992 + 4 === 9007199254740992 + 5 //是否为true?

然后看下面我在调试台下的结果。

为什么会出现这个原因呢?先来探究一下Javascript的Number类型本质了,先来看看最权威的MDN对Javascript数字类型的定义。

重新认识Javascript的数字类型

1. Number类型的定义

MDN中Number的定义,请参见:https://developer.mozilla.org/zh-CN/docs/Glossary/Number

在 JavaScript 中, Number 是一种 定义为 64位双精度浮点型(double-precision 64-bit floating point format) (IEEE 754)的数字数据类型。在其他编程语言中,有不同的数字类型存在,比如:整型(Integers),单精度浮点型(Floats),双精度浮点型(Doubles),大数(Bignums)。

IEEE754类型的值有一个特点,它在介于 -(2^53 -1) 到 2^53-1之间时是精确的,一旦不在这个区间的时候便出现精度问题,所以出现上面问题的原因是Javascript的采用的IEEE754标准而导致的,如果对IEEE754标准规范有兴趣的可看这篇文章。IEEE 754格式是什么?(https://www.zhihu.com/question/21711083)

2. 认识Number值边界

打开控制台,可以看出Number这个构造函数是有几个私有属性的,接下来认识一下这些私有属性。

MAX_VALUE: 该属性是 JavaScript 中可表示的最大的数。它的近似值为 1.7976931348623157e+308。

MIN_VALUE: 该属性是 JavaScript 里最接近 0 的正值,而不是最小的负值。它的值约为 5-324,小于 MIN_VALUE的值将会转换为 0。

MAX_SAFE_INTEGER:它的值精确表示为9007199254740991,这个属性出现的原因正是因为JavaScript使用了IEEE754中指定的双精度浮点格式数字丢失精度,它定义了JavaScript计算中避免丢失精度最大安全数字边界2的53次方- 1。

MIN_SAFE_INTEGER: 它的值精确表示为-9007199254740991, 代表在 JavaScript计算中避免丢失精度的最小安全数字边界 -(2的53次方 - 1).

如何进行一个大于2的53次方的数运算

面试高频考点,俗称“大数相加”,主要考查是否了解JavaScript的数值类型的底层原理,以及超出精度下的超大数计算思想。

1. 解题思路

回想一下我们在小时候刚学数学的时候一个叫竖式运算,一种从个位往前一个一个相加求和的方式,看看下面的图有没有想起来。

我们可以利用这种方式,从末尾一直向前加,当两数相加大于10时便向前进一位,同理我们可以将这里的“大数加法”运算变成两个超大数字从末尾一个一个向前加求和的过程。

2. 代码实现与过程讲解

请仔细查看注释,如有问题请留言。

代码语言:javascript
复制
function bigNumAdd(num1, num2) {
    // 首先检查传来的大数是否是字符串类型,如果传Number类型的大数,在传入的时候已经丢失精度了,
    // 就如 如果传入11111111111111111,处理的时候已经是丢失精度的11111111111111112了,则需要传入
    // 字符串类型的数字 '11111111111111111'
    const checkNum = num => typeof num === 'string' && !isNaN(Number(num))
    if (checkNum(num1) && checkNum(num2)) {
        // 将传入的数据进行反转,从前向后依次加和,模拟个,十,百依次向上加和
        const tmp1 = num1.split('').reverse()
        const tmp2 =  num2.split('').reverse()
        const result = []
        // 格式化函数,主要针对两个大数长度不一致时,超长的数字的格式化为0
        const format = val => {
          if( typeof val === 'number') return val
          if(!isNaN(Number(val))) return Number(val)
          return 0
        }
        let temp = 0
        // 以较长的数字为基准进行从前往后逐个加和,为避免两个数相加最高位进位后,导
        // 致结果长度大于两个数字中的长度,for循环加和长度为最长数字长度加一
        for (let i = 0; i <= Math.max(tmp1.length, tmp2.length); i++) {
          const addTmp = format(tmp1[i]) + format(tmp2[i]) + temp
          // 当加和的数字大于10的情况下,进行进位操作,将要进位的数字赋值给temp,在下一轮使用
          result[i] = addTmp % 10
          temp = addTmp > 9 ? 1 : 0;
        }
        // 计算完成,反转回来
        result.reverse()
        // 将数组for中多加的一位进行处理,如果最高位没有进位则结果第一个数位0,
        // 结果第一个数位1,则发生了进位。 如99+3,最大数字长度位2,结果数长度位3
        // 此时结果的第一位为1,发生了进位,第一位保留,如果是2+94,第一位为0,则不保留第一位
    const resultNum = result[0] > 0
        ? result.join('')
        : result.join('').slice(1)
        console.log('result', resultNum)
    } else {
      return 'big number type error'
    }
}

3. 验证一下

可以验证一下开始时留下的问题。

以上代码均在此处,有兴趣也可拷贝自己调试执行。https://github.com/FantasyGao/About_Node/blob/master/learning/sample/frontend/bigNumAdd/index.js

如果你有需要解析的题目或者想了解的知识点请留言,作者会挑选合适的内容作为后面的文章题材,期待你的留言哦。

如上内容均为自己总结,难免会有错误或者认识偏差,如有问题,希望大家留言指正,以免误人,若有什么问题请留言,会尽力回答之。如果对你有帮助不要忘了分享给你的朋友或者点击右下方的“在看”哦!也可以关注作者,查看历史文章并且关注最新动态,助你早日成为一名全栈工程师!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么2^53和2^53+1相等?
  • 重新认识Javascript的数字类型
  • 如何进行一个大于2的53次方的数运算
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档