Are you an LLM? You can read better optimized documentation at /cat-kit/packages/core/data/number.md for this page in Markdown format
数字操作
介绍
提供货币格式化、精确小数、范围限制与高精度四则运算等功能。n() 用于链式数字处理,$n 提供基于 BigInt 的精确计算工具。
快速使用
typescript
import { n, $n } from '@cat-kit/core'
// 货币格式化
n(1234567.89).currency('CNY', 2) // '1,234,567.89'
// 精确小数
n(3.14159).fixed(2) // '3.14'
// 高精度运算
$n.plus(0.1, 0.2) // 0.3
$n.calc('1 + 2 * (3 + 4)') // 151
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
API参考
货币格式化
typescript
import { n } from '@cat-kit/core'
const amount = n(1234567.89)
// 格式化为人民币
amount.currency('CNY', 2) // '1,234,567.89'
// 格式化为中文大写金额
amount.currency('CNY_HAN', 2)
// '壹佰贰拾叁万肆仟伍佰陆拾柒元捌角玖分'1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
精确小数
typescript
import { n } from '@cat-kit/core'
const num = n(3.14159)
// 精确到指定小数位
num.fixed(2) // '3.14'
num.fixed(0) // '3'1
2
3
4
5
6
7
2
3
4
5
6
7
范围限制
typescript
import { n } from '@cat-kit/core'
n(120).range(0, 100) // 100
n(-5).range(0, 10) // 0
n(5).range(0, 10) // 5
n(10).max(5) // 5
n(1).min(5) // 51
2
3
4
5
6
7
8
2
3
4
5
6
7
8
高精度运算
$n 提供基于 BigInt 的精确四则运算,支持 number 或 string 入参。对于超过 Number.MAX_SAFE_INTEGER 的大数,建议传入 string 以避免解析阶段精度丢失。
typescript
import { $n } from '@cat-kit/core'
// 基础精确运算(解决浮点数精度问题)
$n.plus(0.1, 0.2) // 0.3
$n.minus(1.0, 0.9) // 0.1
$n.mul(19.9, 100) // 1990
$n.div(0.3, 0.1) // 3
// 支持字符串入参,保持大数精度
$n.plus('1234567890123456.1', '0.1')
// 1234567890123456.2
// 表达式计算
$n.calc('1 + 2 * (3 + 4)') // 15
$n.calc('0.1 + 0.2') // 0.31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
交互演示
表达式计算
vue
高精度表达式计算
输入数学表达式,对比 JS 原生计算与 $n.calc 的精度差异
常用示例
JS 原生 Number
0.30000000000000004$n.calc
0.3代码:
$n.calc('0.1 + 0.2')常见问题
0.1 + 0.2→ JS 原生结果为0.30000000000000004,$n.calc 为0.319.9 * 100→ JS 原生结果为1989.9999999999998,$n.calc 为1990- 支持括号、加减乘除四则运算
- 支持负数与科学计数法
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
<template>
<div class="demo-box">
<div class="demo-section">
<h4>高精度表达式计算</h4>
<p class="demo-desc">输入数学表达式,对比 JS 原生计算与 $n.calc 的精度差异</p>
</div>
<div class="demo-controls">
<div class="control-group expression-group">
<label>表达式</label>
<input
v-model="expression"
type="text"
class="demo-input expression-input"
placeholder="例如: 0.1 + 0.2 或 1 + 2 * (3 + 4)"
/>
</div>
<button class="calc-btn" @click="calculate">计算</button>
</div>
<div class="preset-section">
<h5>常用示例</h5>
<div class="preset-chips">
<button
v-for="preset in presets"
:key="preset"
class="preset-chip"
@click="expression = preset"
>
{{ preset }}
</button>
</div>
</div>
<div class="demo-result">
<div class="result-comparison">
<div class="result-item">
<span class="result-tag native">JS 原生 Number</span>
<code :class="['result-value', { error: hasPrecisionIssue }]">{{ nativeResult }}</code>
</div>
<div class="result-arrow">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M5 12h14M12 5l7 7-7 7" />
</svg>
</div>
<div class="result-item">
<span class="result-tag precise">$n.calc</span>
<code class="result-value highlight">{{ calcResult }}</code>
</div>
</div>
<div v-if="calcSteps.length > 0" class="calc-steps">
<h5>计算过程</h5>
<div class="steps-list">
<div v-for="(step, i) in calcSteps" :key="i" class="step-item">
<span class="step-index">{{ i + 1 }}</span>
<code class="step-code">{{ step }}</code>
</div>
</div>
</div>
<div class="code-preview">
<span class="code-label">代码:</span>
<code class="code-value">$n.calc('{{ expression }}')</code>
</div>
</div>
<div class="tips-section">
<h5>常见问题</h5>
<ul class="tips-list">
<li>
<code>0.1 + 0.2</code> → JS 原生结果为 <code>0.30000000000000004</code>,$n.calc 为
<code>0.3</code>
</li>
<li>
<code>19.9 * 100</code> → JS 原生结果为 <code>1989.9999999999998</code>,$n.calc 为
<code>1990</code>
</li>
<li>支持括号、加减乘除四则运算</li>
<li>支持负数与科学计数法</li>
</ul>
</div>
</div>
</template>
<script lang="ts" setup>
import { $n } from '@cat-kit/core'
import { ref, computed } from 'vue'
const expression = ref('0.1 + 0.2')
const calcError = ref('')
const calcSteps = ref<string[]>([])
const presets = [
'0.1 + 0.2',
'1.0 - 0.9',
'19.9 * 100',
'0.3 / 0.1',
'1 + 2 * (3 + 4)',
'1234567890123456.1 + 0.1',
'(0.1 + 0.2) * 10',
'10 - 2.5 * 3 + 1.5'
]
function safeNativeCalc(expr: string): number | null {
const s = expr.replace(/\s+/g, '')
let i = 0
function parseExpression(): number | null {
let left = parseTerm()
if (left === null) return null
while (i < s.length && (s[i] === '+' || s[i] === '-')) {
const op = s[i++]!
const right = parseTerm()
if (right === null) return null
left = op === '+' ? left + right : left - right
}
return left
}
function parseTerm(): number | null {
let left = parseFactor()
if (left === null) return null
while (i < s.length && (s[i] === '*' || s[i] === '/')) {
const op = s[i++]!
const right = parseFactor()
if (right === null) return null
left = op === '*' ? left * right : left / right
}
return left
}
function parseFactor(): number | null {
if (i >= s.length) return null
if (s[i] === '+' || s[i] === '-') {
const sign = s[i] === '-' ? -1 : 1
i++
const val = parseFactor()
return val === null ? null : sign * val
}
if (s[i] === '(') {
i++
const val = parseExpression()
if (val === null || i >= s.length || s[i] !== ')') return null
i++
return val
}
let numStr = ''
while (i < s.length && /[0-9.]/.test(s[i]!)) {
numStr += s[i++]!
}
if (numStr === '' || numStr === '.') return null
const val = Number(numStr)
if (Number.isNaN(val)) return null
return val
}
const result = parseExpression()
return result !== null && i === s.length ? result : null
}
const nativeResult = computed(() => {
const result = safeNativeCalc(expression.value)
if (result !== null) {
return String(result)
}
return '无效表达式'
})
const calcResult = computed(() => {
try {
calcError.value = ''
return String($n.calc(expression.value))
} catch (e: any) {
calcError.value = e.message || '计算错误'
return calcError.value
}
})
const hasPrecisionIssue = computed(() => {
return nativeResult.value !== calcResult.value && !calcError.value
})
function calculate() {
// 触发表达式重新计算(computed 会自动更新)
// 同时生成步骤说明
calcSteps.value = []
try {
const expr = expression.value.trim()
if (!expr) return
// 简单步骤拆解:按运算符分割
const tokens = expr.match(/(\d+\.?\d*|\+|-|\*|\/|\(|\))/g) || []
if (tokens.length > 0) {
calcSteps.value.push(`解析表达式: ${tokens.join(' ')}`)
calcSteps.value.push(`使用 Decimal 高精度计算`)
calcSteps.value.push(`最终结果: ${$n.calc(expr)}`)
}
} catch {
calcSteps.value = []
}
}
</script>
<style scoped>
.demo-box {
display: flex;
flex-direction: column;
gap: 16px;
}
.demo-section h4 {
margin: 0 0 4px 0;
font-size: 16px;
font-weight: 600;
}
.demo-desc {
margin: 0;
font-size: 14px;
color: var(--vp-c-text-2);
}
.demo-controls {
display: flex;
align-items: flex-end;
gap: 12px;
flex-wrap: wrap;
}
.control-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.control-group label {
font-size: 13px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.expression-group {
flex: 1;
min-width: 200px;
}
.expression-input {
width: 100%;
min-width: 280px;
}
.demo-input {
padding: 8px 12px;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
font-size: 14px;
font-family: var(--vp-font-family-mono);
}
.demo-input:focus {
outline: none;
border-color: var(--vp-c-brand-1);
}
.calc-btn {
padding: 8px 20px;
border: none;
border-radius: 6px;
background: var(--vp-c-brand-1);
color: white;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: opacity 0.2s;
}
.calc-btn:hover {
opacity: 0.9;
}
.preset-section h5 {
margin: 0 0 8px 0;
font-size: 13px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.preset-chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.preset-chip {
padding: 4px 10px;
border: 1px solid var(--vp-c-divider);
border-radius: 16px;
background: var(--vp-c-bg);
color: var(--vp-c-text-2);
font-size: 12px;
font-family: var(--vp-font-family-mono);
cursor: pointer;
transition: all 0.2s;
}
.preset-chip:hover {
border-color: var(--vp-c-brand-1);
color: var(--vp-c-brand-1);
}
.demo-result {
background: var(--vp-c-bg-soft);
border-radius: 8px;
padding: 16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.result-comparison {
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
}
.result-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.result-tag {
font-size: 12px;
padding: 2px 8px;
border-radius: 12px;
}
.result-tag.native {
background: var(--vp-c-default-soft);
color: var(--vp-c-text-2);
}
.result-tag.precise {
background: var(--vp-c-brand-soft);
color: var(--vp-c-brand-1);
}
.result-value {
font-size: 16px;
font-weight: 500;
padding: 8px 16px;
background: var(--vp-c-bg);
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
font-family: var(--vp-font-family-mono);
}
.result-value.highlight {
border-color: var(--vp-c-brand-1);
color: var(--vp-c-brand-1);
}
.result-value.error {
border-color: var(--vp-c-warning-1);
color: var(--vp-c-warning-1);
}
.result-arrow {
display: flex;
align-items: center;
color: var(--vp-c-text-2);
}
.result-arrow svg {
color: var(--vp-c-brand-1);
}
.calc-steps h5 {
margin: 0 0 8px 0;
font-size: 13px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.steps-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.step-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 10px;
background: var(--vp-c-bg);
border-radius: 4px;
border: 1px solid var(--vp-c-divider);
}
.step-index {
font-size: 11px;
font-weight: 600;
color: var(--vp-c-brand-1);
background: var(--vp-c-brand-soft);
padding: 1px 6px;
border-radius: 10px;
}
.step-code {
font-size: 13px;
font-family: var(--vp-font-family-mono);
color: var(--vp-c-text-1);
}
.code-preview {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
padding: 10px 12px;
background: var(--vp-c-bg);
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
}
.code-label {
font-size: 13px;
color: var(--vp-c-text-2);
}
.code-value {
font-size: 13px;
font-family: var(--vp-font-family-mono);
color: var(--vp-c-text-1);
}
.tips-section {
padding: 12px 16px;
background: var(--vp-c-tip-soft);
border-radius: 8px;
border-left: 3px solid var(--vp-c-tip-1);
}
.tips-section h5 {
margin: 0 0 8px 0;
font-size: 14px;
font-weight: 600;
color: var(--vp-c-tip-1);
}
.tips-list {
margin: 0;
padding-left: 20px;
font-size: 13px;
color: var(--vp-c-text-2);
display: flex;
flex-direction: column;
gap: 4px;
}
.tips-list code {
font-size: 12px;
background: var(--vp-c-bg);
padding: 1px 4px;
border-radius: 3px;
}
</style>
数字格式化
vue
数字格式化演示
输入数字并选择格式化方式,实时查看结果
示例数值
格式化结果:
1,234,567.89代码:
n(1234567.89).currency('CNY', 2)可用方法速查
n(val).currency('CNY', 2)人民币千分位n(val).currency('CNY_HAN', 2)中文大写金额n(val).fixed(2)指定小数位数$n.formatter({ style: 'percent' })百分比格式器123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
<template>
<div class="demo-box">
<div class="demo-section">
<h4>数字格式化演示</h4>
<p class="demo-desc">输入数字并选择格式化方式,实时查看结果</p>
</div>
<div class="demo-controls">
<div class="control-group">
<label>输入数字</label>
<input
v-model="inputValue"
type="text"
class="demo-input number-input"
placeholder="例如: 1234567.89"
/>
</div>
<div class="control-group">
<label>格式化类型</label>
<select v-model="formatType" class="demo-select">
<option value="currency-cny">人民币 (CNY)</option>
<option value="currency-han">中文大写 (CNY_HAN)</option>
<option value="fixed">精确小数 (fixed)</option>
<option value="formatter-decimal">千分位 (decimal)</option>
<option value="formatter-percent">百分比 (percent)</option>
</select>
</div>
<div v-if="showPrecision" class="control-group">
<label>小数位数</label>
<input
v-model.number="precision"
type="number"
min="0"
max="10"
class="demo-input precision-input"
/>
</div>
</div>
<div class="preset-section">
<h5>示例数值</h5>
<div class="preset-chips">
<button
v-for="preset in numberPresets"
:key="preset"
class="preset-chip"
@click="inputValue = preset"
>
{{ preset }}
</button>
</div>
</div>
<div class="demo-result">
<div class="result-row">
<span class="result-label">格式化结果:</span>
<code class="result-value">{{ formattedResult }}</code>
</div>
<div class="result-row code-preview">
<span class="result-label">代码:</span>
<code class="result-code">{{ codePreview }}</code>
</div>
</div>
<div class="methods-section">
<h5>可用方法速查</h5>
<div class="methods-grid">
<div class="method-item">
<code>n(val).currency('CNY', 2)</code>
<span>人民币千分位</span>
</div>
<div class="method-item">
<code>n(val).currency('CNY_HAN', 2)</code>
<span>中文大写金额</span>
</div>
<div class="method-item">
<code>n(val).fixed(2)</code>
<span>指定小数位数</span>
</div>
<div class="method-item">
<code>$n.formatter({ style: 'percent' })</code>
<span>百分比格式器</span>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { n, $n } from '@cat-kit/core'
import { ref, computed } from 'vue'
const inputValue = ref('1234567.89')
const formatType = ref('currency-cny')
const precision = ref(2)
const showPrecision = computed(() => {
return [
'currency-cny',
'currency-han',
'fixed',
'formatter-decimal',
'formatter-percent'
].includes(formatType.value)
})
const numberPresets = ['1234567.89', '0.1', '3.1415926', '-9999.99', '1000000', '0.005']
const numValue = computed(() => {
const v = parseFloat(inputValue.value)
return Number.isNaN(v) ? 0 : v
})
const formattedResult = computed(() => {
try {
switch (formatType.value) {
case 'currency-cny':
return n(numValue.value).currency('CNY', precision.value)
case 'currency-han':
return n(numValue.value).currency('CNY_HAN', precision.value)
case 'fixed':
return n(numValue.value).fixed(precision.value)
case 'formatter-decimal': {
const formatter = $n.formatter({ style: 'decimal', precision: precision.value })
return formatter.format(numValue.value)
}
case 'formatter-percent': {
const formatter = $n.formatter({ style: 'percent', precision: precision.value })
return formatter.format(numValue.value)
}
default:
return ''
}
} catch {
return '格式化错误'
}
})
const codePreview = computed(() => {
switch (formatType.value) {
case 'currency-cny':
return `n(${numValue.value}).currency('CNY', ${precision.value})`
case 'currency-han':
return `n(${numValue.value}).currency('CNY_HAN', ${precision.value})`
case 'fixed':
return `n(${numValue.value}).fixed(${precision.value})`
case 'formatter-decimal':
return `$n.formatter({ style: 'decimal', precision: ${precision.value} }).format(${numValue.value})`
case 'formatter-percent':
return `$n.formatter({ style: 'percent', precision: ${precision.value} }).format(${numValue.value})`
default:
return ''
}
})
</script>
<style scoped>
.demo-box {
display: flex;
flex-direction: column;
gap: 16px;
}
.demo-section h4 {
margin: 0 0 4px 0;
font-size: 16px;
font-weight: 600;
}
.demo-desc {
margin: 0;
font-size: 14px;
color: var(--vp-c-text-2);
}
.demo-controls {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: flex-end;
}
.control-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.control-group label {
font-size: 13px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.demo-input,
.demo-select {
padding: 8px 12px;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
font-size: 14px;
}
.number-input {
min-width: 140px;
font-family: var(--vp-font-family-mono);
}
.precision-input {
width: 80px;
text-align: center;
}
.demo-input:focus,
.demo-select:focus {
outline: none;
border-color: var(--vp-c-brand-1);
}
.preset-section h5 {
margin: 0 0 8px 0;
font-size: 13px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.preset-chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.preset-chip {
padding: 4px 10px;
border: 1px solid var(--vp-c-divider);
border-radius: 16px;
background: var(--vp-c-bg);
color: var(--vp-c-text-2);
font-size: 12px;
font-family: var(--vp-font-family-mono);
cursor: pointer;
transition: all 0.2s;
}
.preset-chip:hover {
border-color: var(--vp-c-brand-1);
color: var(--vp-c-brand-1);
}
.demo-result {
background: var(--vp-c-bg-soft);
border-radius: 8px;
padding: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
.result-row {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.result-label {
font-size: 13px;
color: var(--vp-c-text-2);
min-width: 80px;
}
.result-value {
font-size: 18px;
font-weight: 600;
color: var(--vp-c-brand-1);
background: var(--vp-c-brand-soft);
padding: 4px 12px;
border-radius: 4px;
}
.result-code {
font-size: 13px;
color: var(--vp-c-text-1);
background: var(--vp-c-bg);
padding: 6px 10px;
border-radius: 4px;
border: 1px solid var(--vp-c-divider);
font-family: var(--vp-font-family-mono);
}
.methods-section h5 {
margin: 0 0 8px 0;
font-size: 13px;
font-weight: 500;
color: var(--vp-c-text-2);
}
.methods-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 10px;
}
.method-item {
display: flex;
flex-direction: column;
gap: 4px;
padding: 10px;
background: var(--vp-c-bg-soft);
border-radius: 6px;
border: 1px solid var(--vp-c-divider);
}
.method-item code {
font-size: 12px;
font-family: var(--vp-font-family-mono);
color: var(--vp-c-brand-1);
}
.method-item span {
font-size: 12px;
color: var(--vp-c-text-2);
}
</style>
