dsh-math-calculator 2.3.3 → 2.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -3810,7 +3810,7 @@ export function runMathTool(rawArguments: unknown): MathResult {
3810
3810
  if (hintNote) result.notice = hintNote
3811
3811
 
3812
3812
  /** 调低计算精度导致结果被截 —— 那种「看起来正常、末位是假的」情况。 */
3813
- const truncNote = checkPrecisionTruncation(requestedPrecision, precision, result.result)
3813
+ const truncNote = checkPrecisionTruncation(requestedPrecision, precision, result.result, input)
3814
3814
  if (truncNote) {
3815
3815
  result.notice = result.notice ? result.notice + '\n' + truncNote : truncNote
3816
3816
  }
@@ -4602,6 +4602,7 @@ function checkPrecisionTruncation(
4602
4602
  requested: number | undefined,
4603
4603
  effective: number,
4604
4604
  text: string,
4605
+ source: string,
4605
4606
  ): string {
4606
4607
  /**
4607
4608
  * 判据:调用方显式给的精度**低于默认 64** 才算「调低了」,
@@ -4611,15 +4612,62 @@ function checkPrecisionTruncation(
4611
4612
  * (effective 就是 clamp 过的 requested),于是这条永远不触发。
4612
4613
  */
4613
4614
  if (requested === undefined || requested >= DEFAULT_PRECISION) return ''
4614
- /** 只看纯数字结果:带单位的另有提示通道。 */
4615
+ /**
4616
+ * ## 两个判据,缺一不可(本轮补的是第二个)
4617
+ *
4618
+ * ① **结果装不下**:整数位数 > precision,末几位被截。
4619
+ * ② **表达式本身超出精度**:即使结果看起来很小或干脆是 0。
4620
+ *
4621
+ * ★ ② 是本轮实测抓到的漏洞(外部反馈文档 2.3):
4622
+ *
4623
+ * ```
4624
+ * (10^20+1)-(10^20), precision=20 → 0 ok=true, notice 空
4625
+ * ```
4626
+ *
4627
+ * 结果被截成了 **0** —— 而 0 只有 1 位,判据① 立刻放行。
4628
+ * 于是「两个 20 位数相减、结果被算成 0」这种最危险的失真,
4629
+ * 恰恰因为结果太小而绕过了检查。
4630
+ *
4631
+ * 这类「结果越离谱、越容易逃过检查」的结构必须堵掉,
4632
+ * 所以改成**看输入而不只看输出**:表达式里出现 10^n(n 超过 precision)
4633
+ * 或长整数串时,无论结果多小都提示。
4634
+ */
4615
4635
  const numeric = text.trim().match(/^-?([0-9]+)(?:\.([0-9]+))?$/)
4616
- if (!numeric) return ''
4617
- const integerDigits = numeric[1].replace(/^0+(?=.)/, '').length
4618
- if (integerDigits <= effective) return ''
4619
- return '本次结果有 ' + integerDigits + ' 位有效数字,而 precision=' + effective +
4620
- ' 只能精确表示 ' + effective + ' 位——末 ' + (integerDigits - effective) +
4621
- ' 位可能已失真。需要精确值时请用 precision:64(默认),' +
4622
- '只想让输出短些请用 displayPrecision。'
4636
+ const integerDigits = numeric ? numeric[1].replace(/^0+(?=.)/, '').length : 0
4637
+ const resultOverflows = numeric !== null && integerDigits > effective
4638
+ /**
4639
+ * 表达式里是否有超出 precision 的大数。
4640
+ *
4641
+ * 两种形态:
4642
+ * · 幂字面量 `10^20`、`1e20`
4643
+ * · 长整数串(连续 16 位以上数字)
4644
+ *
4645
+ * 阈值用 effective(而不是固定值):precision=20 时 `10^20` 是边界,
4646
+ * `10^21` 才必然丢位;而 precision=8 时 `10^9` 就丢。
4647
+ */
4648
+ /**
4649
+ * 临界点是 `power >= effective`,不是 `>`。
4650
+ *
4651
+ * 因为 `10^20` 是「1 后跟 20 个零」= **21 位**数字,
4652
+ * precision=20 只能表示 20 位有效数字,装不下它。
4653
+ * 所以 precision=20 时 `10^20` 已在边界上,`10^20+1` 里的 +1 必然被吃掉。
4654
+ *
4655
+ * 第一版写成 `>` 时,`(10^20+1)-(10^20)` 恰好卡在边界外,漏掉了。
4656
+ */
4657
+ const powerMatch = source.match(/10\s*\^\s*(\d+)/)
4658
+ const powerBeyond = powerMatch ? Number(powerMatch[1]) >= effective : false
4659
+ /** 科学记数法字面量(`1e20`)同样算大数。 */
4660
+ const expMatch = source.match(/[0-9][0-9.]*[eE]\s*(\d+)/)
4661
+ const expBeyond = expMatch ? Number(expMatch[1]) >= effective : false
4662
+ const longDigits = /(^|[^0-9.eE+-])[0-9]{16,}(?![0-9])/.test(source)
4663
+ const sourceOverflows = powerBeyond || expBeyond || longDigits
4664
+ if (!resultOverflows && !sourceOverflows) return ''
4665
+ const reason = resultOverflows
4666
+ ? '本次结果有 ' + integerDigits + ' 位有效数字,precision=' + effective +
4667
+ ' 只能精确表示 ' + effective + ' 位——末 ' + (integerDigits - effective) + ' 位可能已失真。'
4668
+ : '表达式里出现了超出 precision=' + effective + ' 能表示的大数,结果可能已失真' +
4669
+ (numeric !== null ? '(本次结果 ' + text.trim() + ')' : '') + '。'
4670
+ return reason + '需要精确值时请用 precision:64(默认),只想让输出短些请用 displayPrecision。'
4623
4671
  }
4624
4672
 
4625
4673
  /**