上次写程序调试整数溢出,对着屏幕反复确认2的31次方等于多少,折腾半天差点把整段代码推倒重写。本来以为就是简单乘几次,随手拿计算器敲了两下,结果第一次出来的数字不对,脑子一下就卡壳了,盯着那串数字反复核对,心里还在嘀咕是不是哪里输错了。
写C语言的时候,int类型占四个字节,有符号的int最高位是符号位,剩下31位用来存数值,正数上限刚好卡在2的31次方减一,也就是2147483647,多往上加一直接就溢出变负数,那会就是卡在这个地方,分不清2的31次方本身和int最大值,两个数字挨得太近,很容易搞混。
最开始偷懒,打算一步步手动相乘。2的10次方是1024,这个是记熟的,那2的20次方就是1024乘1024,算出来1048576,再乘1024得到2的30次方,等于1073741824。算到这一步手都有点飘,再乘一个2,就是2147483648。
就差最后一步核对。
打开系统自带计算器,切换到科学计算模式,输入2,点x的y次方,输入31,按下等号,屏幕上稳稳跳出2147483648。这下才算踏实,之前代码报错,就是因为变量定义成有符号整型,直接赋值这个数,编译器直接标红,一直没反应过来这两个数之间差1的区别,还以为是代码逻辑哪里写错,反复排查循环条件,白白耗掉半个钟头。
旁边同事路过,扫了一眼屏幕,随口说好多新人都会在这里踩同一个坎,把2的31次方和有符号int最大值弄成同一个数字,写接口传参、数据库存数字的时候,经常因为这个细节出问题,数据库字段选int,存不下2147483648,必须改成bigint,之前项目里就出过线上bug,就是开发没留意这个边界,大量数据入库失败,排查日志的时候,花了很久才定位到数值超出范围这件事。
那时候翻文档,文档里写的也比较简略,只标注有符号32位整数范围从负2147483648到2147483647,看着这两个数字,才反应过来负的极限刚好是负的2的31次方,正数极限是2的31次方减一,两边并不对称,之前一直想当然以为正负的绝对值一样,这个认知偏差,才是这次卡住的根源。
反复心算验证几遍,2的10次方1024,2的20次方1048576,2的30次方1073741824,乘以2得到2147483648,没有算错。拿草稿纸上写下来,一串数字逐位看,2147483648,十个数字,记的时候可以拆两段记,214748和3648,不容易记混,反正以后再碰到32位整型溢出的场景,脑子里第一时间就跳出这串数字,不用每次都重新从头计算。
后面改代码,直接把变量类型调整成无符号整型或者长整型,再把数值填进去,编译器不再报错,测试用例跑通,那些之前一直失败的边界测试全部正常通过。这件事看着只是算一个幂次,可一旦放到代码里,就不是单纯的数学题,一个数字的差别,就能引发线上故障,很多人觉得这种基础数值随时可以查,没必要记住,可调试的时候网络不通,没法打开网页检索,手里只有本地计算器,提前理清这个边界,就能省下不少等待查询的时间。
关掉计算器窗口,把草稿纸揉成团丢进垃圾桶。