2024-04-30
注意力训练 sub
要求不使用减号的情况下实现减法运算
sub(a:number, b:number): string
只需要考虑 16 位整数的加减法, 测试用例如下:
  1. 1.
    sub(1000, 2) ==> '998'
  2. 2.
    sub(2, 1000) ==> '-998'
  3. 3.
    sub(100, 100) ==> '0'
  4. 4.
    sub(0, 0) ==> '0'
  5. 5.
    sub(0, 2) ==> '-2'
  6. 6.
    sub(-32768, 1) ==> '32767' (有符号数负溢出)

# 实现

依据二进制补码的数学原理可以写出:
00// 推荐位运算相关都使用 bigint 来实现,
01// bigint 有更好的整数特性, 比 number 的浮点数特性好得多
02
03/** 转补码, 并将结果对齐到 uint16 */
04function complement(v: bigint): bigint {
05  return ((~v) + 1n) & 0xffffn;
06}
07
08export function ecznSub(a: number, b: number): string {
09  const an = BigInt(a); // 转成 bigint
10  const bn = BigInt(b); // 转成 bigint
11
12  // 计算结果
13  const result = (an + complement(bn)) & 0xffffn;
14
15  // 判断符号位, 转成字符串返回
16  if (result & 0x8000n) return `-${complement(result)}`;
17
18  return `${result}`;
19}
20
21console.group('开始 sub 测试用例');
22  console.log(`sub(1000, 2) ==> ${ecznSub(1000, 2)}`)
23  console.log(`sub(2, 1000) ==> ${ecznSub(2, 1000)}`)
24  console.log(`sub(100, 100) ==> ${ecznSub(100, 100)}`)
25  console.log(`sub(0, 0) ==> ${ecznSub(0, 0)}`)
26  console.log(`sub(0, 2) ==> ${ecznSub(0, 2)}`)
27  console.log(`sub(-32768, 1) ==> ${ecznSub(-32768, 1)}`)
28console.groupEnd();
备注: 我已将 ecznSub 挂在 window 上了,可以自行打开 devtools 进行调试

# 注意到...

当程序执行读取某个负数的时候,其实是加载了一个对应相反数的二进制补码,因此 -2 & 0xff 结果是 0b11111110 为 254
因此如果给你一段内存,从字节流离很难判断说这个到底是不是正数,因为有符号数是被定义出来的,从外表上看跟一个无符号数一模一样。
另外,建议位运算都用 BigInt 来完成,有比普通 number 更好的整数特性