大数加法(含小数)

大数加法(含小数)

题目描述

实现两个大数相加,大数可能包含小数位。输入两个数字字符串 numStr1、numStr2,返回它们的和的字符串表示。

数据范围:数字字符串长度可能非常大,无法用内置整数类型直接存储,必须用字符串模拟。

示例

输入:
9007199254740991
1234567899999999999

输出:
1243575099254740990
输入:
9007199254740990.12
1234567899999999999

输出:
1243575099254740989.12

核心思路

大数加法的本质:模拟竖式加法,从最低位(最右侧)往最高位逐位相加,处理进位。

含小数时的关键步骤:

  1. 拆分整数部分和小数部分:以 '.' 为界切分;
  2. 小数部分右补 0:让两个数的小数位长度一致,方便逐位对齐;
  3. 整数部分左补 0:让两个数的整数位长度一致;
  4. 拼接后从右往左逐位相加,用 carry 处理进位;
  5. 还原小数点:根据小数部分长度 n,在结果中切出整数部分和小数部分;
  6. 处理前导 0 和末尾 0。

原始代码(62% 通过率)

class Solution:
    def bigAdd(self, numStr1, numStr2):
        if '.' in numStr1:
            a0, a1 = numStr1.split('.')
        else:
            a0, a1 = numStr1, ''
        if '.' in numStr2:
            b0, b1 = numStr2.split('.')
        else:
            b0, b1 = numStr2, ''

        # 小数部分右侧补 0
        n = max(len(a1), len(b1))
        a1 = a1.ljust(n, '0')
        b1 = b1.ljust(n, '0')

        # 整数部分左侧补 0
        m = max(len(a0), len(b0))
        a0 = a0.rjust(m, '0')
        b0 = b0.rjust(m, '0')

        aa = a0 + a1
        bb = b0 + b1

        N = m + n
        ans = []
        carry = 0

        for i in range(N - 1, -1, -1):
            x = int(aa[i])
            y = int(bb[i])
            total = x + y + carry
            carry, cur = divmod(total, 10)
            ans.append(str(cur))

        if carry:
            ans.append(str(carry))

        ans.reverse()
        res = ''.join(ans)

        if n > 0:
            int_part = res[:-n]
            frac_part = res[-n:]
            int_part = int_part.lstrip('0') or '0'
            return int_part + '.' + frac_part
        else:
            res = res.lstrip('0') or '0'
            return res

原始代码的问题(为什么只有 62%)

问题 1:小数部分末尾多余的 0 没有去掉

反例:"1.5" + "2.5" = "4.0",代码返回 "4.0",但通常期望 "4"。

反例:"1.25" + "2.75" = "4.00",代码返回 "4.00",期望 "4"。

反例:"0.5" + "0.4" = "0.9" 返回 "0.9" 正确,但 "0.5" + "0.5" = "1.0" 返回 "1.0",期望 "1"。

问题 2:小数部分全 0 时小数点没有去掉

"1.00" + "2.00" = "3.00",应该返回 "3",而不是 "3.00"。

问题 3:整数部分为空字符串的边界(较少见但要注意)

输入 ".5" 时 a0 = "",如果两数整数部分都为空,m = 0,a0 = "",虽然代码能跑,但 res[:-n] 可能得到空串,靠 lstrip('0') or '0' 兜底,正确但依赖运气。

问题 4:多个小数点或非法输入

"1.2.3" 这种输入 split('.') 会返回 3 个元素,直接报错。题目一般不测,但严谨起见可以处理。

修正后的代码

class Solution:
    def bigAdd(self, numStr1: str, numStr2: str) -> str:
        # 1. 拆分整数部分和小数部分
        if '.' in numStr1:
            a0, a1 = numStr1.split('.', 1)
        else:
            a0, a1 = numStr1, ''
        if '.' in numStr2:
            b0, b1 = numStr2.split('.', 1)
        else:
            b0, b1 = numStr2, ''

        # 2. 小数部分右补 0
        n = max(len(a1), len(b1))
        a1 = a1.ljust(n, '0')
        b1 = b1.ljust(n, '0')

        # 3. 整数部分左补 0
        m = max(len(a0), len(b0), 1)     # 至少 1 位,避免空串
        a0 = a0.rjust(m, '0')
        b0 = b0.rjust(m, '0')

        aa = a0 + a1
        bb = b0 + b1
        N = m + n

        # 4. 从右往左逐位相加
        ans = []
        carry = 0
        for i in range(N - 1, -1, -1):
            total = int(aa[i]) + int(bb[i]) + carry
            carry, cur = divmod(total, 10)
            ans.append(str(cur))

        if carry:
            ans.append(str(carry))

        ans.reverse()
        res = ''.join(ans)

        # 5. 还原小数点
        if n > 0:
            int_part = res[:-n]
            frac_part = res[-n:]

            # 去掉整数部分的前导 0
            int_part = int_part.lstrip('0') or '0'
            # 去掉小数部分的末尾 0
            frac_part = frac_part.rstrip('0')

            if frac_part:
                return int_part + '.' + frac_part
            else:
                return int_part
        else:
            return res.lstrip('0') or '0'

修正点对照

问题 修正
小数末尾多余的 0 frac_part = frac_part.rstrip('0')
小数全 0 时残留小数点 if frac_part: return int_part + '.' + frac_part; else: return int_part
整数部分可能为空串 m = max(len(a0), len(b0), 1) 保证至少 1 位
多个小数点 split('.', 1) 只切第一个点

完整可运行代码

class Solution:
    def bigAdd(self, numStr1: str, numStr2: str) -> str:
        if '.' in numStr1:
            a0, a1 = numStr1.split('.', 1)
        else:
            a0, a1 = numStr1, ''
        if '.' in numStr2:
            b0, b1 = numStr2.split('.', 1)
        else:
            b0, b1 = numStr2, ''

        n = max(len(a1), len(b1))
        a1 = a1.ljust(n, '0')
        b1 = b1.ljust(n, '0')

        m = max(len(a0), len(b0), 1)
        a0 = a0.rjust(m, '0')
        b0 = b0.rjust(m, '0')

        aa = a0 + a1
        bb = b0 + b1
        N = m + n

        ans = []
        carry = 0
        for i in range(N - 1, -1, -1):
            total = int(aa[i]) + int(bb[i]) + carry
            carry, cur = divmod(total, 10)
            ans.append(str(cur))

        if carry:
            ans.append(str(carry))

        ans.reverse()
        res = ''.join(ans)

        if n > 0:
            int_part = res[:-n]
            frac_part = res[-n:]

            int_part = int_part.lstrip('0') or '0'
            frac_part = frac_part.rstrip('0')

            if frac_part:
                return int_part + '.' + frac_part
            else:
                return int_part
        else:
            return res.lstrip('0') or '0'


if __name__ == '__main__':
    s = Solution()
    numStr1 = input().strip()
    numStr2 = input().strip()
    print(s.bigAdd(numStr1, numStr2))

测试用例

numStr1 numStr2 期望输出 原始代码 修正后
"9007199254740991" "1234567899999999999" "1243575099254740990" ✓ ✓
"9007199254740990.12" "1234567899999999999" "1243575099254740989.12" ✓ ✓
"1.5" "2.5" "4" "4.0" ✗ "4" ✓
"1.25" "2.75" "4" "4.00" ✗ "4" ✓
"0.5" "0.5" "1" "1.0" ✗ "1" ✓
"0.5" "0.4" "0.9" ✓ ✓
"0.0" "0.0" "0" "0.0" ✗ "0" ✓
"0" "0" "0" ✓ ✓
"123.456" "0" "123.456" ✓ ✓
".5" ".5" "1" "1.0" ✗ "1" ✓

复杂度分析

  • 时间复杂度:O(n)O(n),其中 n=max⁡(len(numStr1),len(numStr2))n = \max(\text{len}(numStr1), \text{len}(numStr2))。每位相加一次。
  • 空间复杂度:O(n)O(n),存储结果和补齐后的字符串。

关键细节

1. 为什么先补 0 再相加

  • 小数部分右补 0:"1.5" 和 "2.25",补成 "1.50" 和 "2.25",小数点对齐;
  • 整数部分左补 0:"12" 和 "3",补成 "12" 和 "03",位数对齐。

补齐后,两个拼接字符串长度相等,可以直接从右往左逐位相加。

2. 进位在最高位时多一位

如果最后还有进位,需要额外追加一位。例如 "99" + "1" = "100",res 的长度比 N 多 1。这不会影响 res[:-n] 和 res[-n:] 的切分,因为进位加在整数部分的最前面。

3. 前导 0 和末尾 0 的处理

  • 整数部分前导 0:"001" → "1",用 lstrip('0'),但要防止全 0 变成空串,用 or '0' 兜底;
  • 小数部分末尾 0:"1.500" → "1.5",用 rstrip('0');
  • 小数部分全 0:"1.000" → "1",rstrip('0') 后为空串,不拼接小数点。

4. 空整数部分的处理

输入 ".5" 时整数部分为空串,如果直接 rjust(m, '0') 且 m = 0,结果还是空串,res[:-n] 可能得到空串。加 max(..., 1) 保证至少 1 位,避免边界问题。

5. 多个小数点的容错

numStr1.split('.', 1) 只切第一个 .,剩下的部分作为小数部分。如果输入 "1.2.3" 这种非法输入,小数部分会变成 "2.3",后续 int() 会报错。如果需要严格容错,可以在前面加校验,但题目一般不测。

总结

  • 本质:字符串模拟竖式加法,处理进位;
  • 拆分:以 . 为界拆成整数部分和小数部分;
  • 对齐:小数部分右补 0,整数部分左补 0;
  • 相加:从右往左逐位相加,carry 处理进位;
  • 还原:按小数部分长度切出整数和小数,去掉前导 0 和末尾 0;
  • 易错点:小数末尾的 0 要去掉,小数全 0 时要去掉小数点;
  • 通用套路:大数运算 → 字符串模拟竖式,按位处理进位。

相关题目

  • LC 415. 字符串相加(整数大数加法)
  • LC 43. 字符串相乘(大数乘法)
  • LC 2. 两数相加(链表版大数加法)
  • LC 67. 二进制求和(二进制大数加法)
  • LC 989. 数组形式的整数加法(数组版大数加法)

大数加法(含小数)
https://mingsm17518.github.io/2026/10/25/刷题笔记/机考/虾皮/01_大数加法(含小数)/
作者
Ming
发布于
2026年10月25日
更新于
2026年10月25日
许可协议