HarmonyOS 鸿蒙Next数学类上架项目解析8-小数转分数算法
HarmonyOS 鸿蒙Next数学类上架项目解析8-小数转分数算法 在开发鸿蒙数学计算应用时,需要将计算结果从小数形式转换为分数形式显示。要求:
- 能够将有限小数转换为最简分数
- 支持设置精度容差
- 处理特殊情况(整数、负数、无限循环小数近似)
如何用ArkTS实现一个小数转分数的算法?
小数转分数的核心算法是连分数展开法(Continued Fraction),它能够找到最接近给定小数的最简分数。
算法原理:
1. 取小数的整数部分
2. 取倒数后重复步骤1
3. 根据展开序列构造分数
1. 定义数据模型
// 分数接口
interface Fraction {
numerator: number // 分子
denominator: number // 分母
isNegative: boolean // 是否为负数
}
// 转换结果接口
interface FractionResult {
fraction: Fraction
display: string // 显示字符串
decimal: number // 原始小数
error: number // 误差
isValid: boolean
errorMsg?: string
}
2. 核心转换函数
/ 计算最大公约数(欧几里得算法) @param a 整数a @param b 整数b @returns 最大公约数 /
function gcd(a: number, b: number): number {
a = Math.abs(Math.round(a))
b = Math.abs(Math.round(b))
while (b !== 0) {
const temp: number = b
b = a % b
a = temp
}
return a
}
/ 约分(化简分数) @param numerator 分子 @param denominator 分母 @returns 最简分数 /
function simplifyFraction(numerator: number, denominator: number): Fraction {
const isNegative: boolean = (numerator < 0) !== (denominator < 0)
numerator = Math.abs(numerator)
denominator = Math.abs(denominator)
const divisor: number = gcd(numerator, denominator)
return {
numerator: numerator / divisor,
denominator: denominator / divisor,
isNegative: isNegative
}
}
/ 使用连分数算法将小数转换为分数 @param decimal 小数 @param tolerance 精度容差(默认1e-10) @param maxIterations 最大迭代次数(默认20) @returns 转换结果 /
function decimalToFraction(
decimal: number,
tolerance: number = 1e-10,
maxIterations: number = 20
): FractionResult {
// 验证输入
if (Number.isNaN(decimal) || !Number.isFinite(decimal)) {
return {
fraction: { numerator: 0, denominator: 1, isNegative: false },
display: 'NaN',
decimal: decimal,
error: 0,
isValid: false,
errorMsg: '请输入有效数字'
}
}
// 处理负数
const isNegative: boolean = decimal < 0
let value: number = Math.abs(decimal)
// 处理整数
if (Math.abs(value - Math.round(value)) < tolerance) {
const intValue: number = Math.round(value)
return {
fraction: { numerator: intValue, denominator: 1, isNegative: isNegative },
display: isNegative ? `-${intValue}` : `${intValue}`,
decimal: decimal,
error: 0,
isValid: true
}
}
// 连分数展开
let numerator: number = 1
let denominator: number = 0
let prevNumerator: number = 0
let prevDenominator: number = 1
let remainder: number = value
for (let i = 0; i < maxIterations; i++) {
const intPart: number = Math.floor(remainder)
// 更新分子分母
const tempNumerator: number = numerator
numerator = intPart * numerator + prevNumerator
prevNumerator = tempNumerator
const tempDenominator: number = denominator
denominator = intPart * denominator + prevDenominator
prevDenominator = tempDenominator
// 检查精度
const currentValue: number = numerator / denominator
if (Math.abs(currentValue - value) < tolerance) {
break
}
// 计算下一个余数
const fractionalPart: number = remainder - intPart
if (fractionalPart < tolerance) {
break
}
remainder = 1 / fractionalPart
// 防止溢出
if (!Number.isFinite(remainder)) {
break
}
}
// 约分
const fraction: Fraction = simplifyFraction(numerator, denominator)
fraction.isNegative = isNegative
// 生成显示字符串
let display: string
if (fraction.denominator === 1) {
display = isNegative ? `-${fraction.numerator}` : `${fraction.numerator}`
} else {
display = isNegative
? `-${fraction.numerator}/${fraction.denominator}`
: `${fraction.numerator}/${fraction.denominator}`
}
// 计算误差
const resultValue: number = fraction.numerator / fraction.denominator
const error: number = Math.abs(resultValue - value)
return {
fraction: fraction,
display: display,
decimal: decimal,
error: error,
isValid: true
}
}
3. 常见分数识别
/ 识别常见分数(如 1/2, 1/3, 1/4 等) @param decimal 小数 @param tolerance 容差 @returns 识别结果或null /
function recognizeCommonFraction(decimal: number, tolerance: number = 1e-6): string | null {
const isNegative: boolean = decimal < 0
const value: number = Math.abs(decimal)
// 常见分数表
const commonFractions: Array<[number, string]> = [
[0.5, '1/2'],
[0.333333, '1/3'],
[0.666667, '2/3'],
[0.25, '1/4'],
[0.75, '3/4'],
[0.2, '1/5'],
[0.4, '2/5'],
[0.6, '3/5'],
[0.8, '4/5'],
[0.166667, '1/6'],
[0.833333, '5/6'],
[0.142857, '1/7'],
[0.125, '1/8'],
[0.375, '3/8'],
[0.625, '5/8'],
[0.875, '7/8'],
[0.1, '1/10'],
[0.3, '3/10'],
[0.7, '7/10'],
[0.9, '9/10']
]
for (const [fracValue, fracStr] of commonFractions) {
if (Math.abs(value - fracValue) < tolerance) {
return isNegative ? `-${fracStr}` : fracStr
}
}
return null
}
/
智能转换(优先识别常见分数)
/
function smartDecimalToFraction(decimal: number): FractionResult {
// 先尝试识别常见分数
const common = recognizeCommonFraction(decimal)
if (common) {
const result = decimalToFraction(decimal)
result.display = common
return result
}
return decimalToFraction(decimal)
}
4. 完整UI组件实现
@Entry
@Component
struct FractionConverter {
@State decimalInput: string = '0.75'
@State toleranceInput: string = '0.0000001'
@State result: FractionResult | null = null
build() {
Column() {
Text('小数转分数')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
Text('连分数展开算法')
.fontSize(14)
.fontColor('#666666')
.margin({ bottom: 20 })
// 小数输入
Row() {
Text('小数:')
TextInput({ text: this.decimalInput })
.width(150)
.type(InputType.Number)
.onChange((value: string) => { this.decimalInput = value })
}
.margin({ bottom: 10 })
// 精度输入
Row() {
Text('精度:')
TextInput({ text: this.toleranceInput })
.width(150)
.onChange((value: string) => { this.toleranceInput = value })
}
.margin({ bottom: 20 })
// 转换按钮
Button('转换为分数')
.onClick(() => this.handleConvert())
.width(200)
.margin({ bottom: 20 })
// 结果显示
if (this.result) {
Column() {
if (this.result.isValid) {
Text(`${this.result.decimal}`)
.fontSize(18)
.margin({ bottom: 8 })
Text('=')
.fontSize(24)
.margin({ bottom: 8 })
Text(this.result.display)
.fontSize(32)
.fontWeight(FontWeight.Bold)
.fontColor('#007AFF')
.margin({ bottom: 12 })
Text(`分子:${this.result.fraction.numerator}`)
.fontSize(14)
.fontColor('#666666')
Text(`分母:${this.result.fraction.denominator}`)
.fontSize(14)
.fontColor('#666666')
Text(`误差:${this.result.error.toExponential(4)}`)
.fontSize(12)
.fontColor('#999999')
.margin({ top: 8 })
} else {
Text(this.result.errorMsg || '转换失败')
.fontColor(Color.Red)
}
}
.padding(20)
.backgroundColor('#f5f5f5')
.borderRadius(8)
}
// 常见分数参考
Column() {
Text('常见分数参考:')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 8 })
Text('1/2 = 0.5 1/3 ≈ 0.333 1/4 = 0.25')
.fontSize(12)
.fontColor('#666666')
Text('1/5 = 0.2 1/6 ≈ 0.167 1/8 = 0.125')
.fontSize(12)
.fontColor('#666666')
}
.margin({ top: 30 })
.padding(16)
.backgroundColor('#f0f0f0')
.borderRadius(8)
}
.width('100%')
.height('100%')
.padding(20)
}
handleConvert(): void {
const decimal: number = parseFloat(this.decimalInput)
const tolerance: number = parseFloat(this.toleranceInput)
this.result = decimalToFraction(decimal, tolerance)
}
}
5. 使用示例
// 示例1:简单分数 const result1 = decimalToFraction(0.5) // { display: ‘1/2’, fraction: { numerator: 1, denominator: 2 } }
// 示例2:循环小数 const result2 = decimalToFraction(0.333333) // { display: ‘1/3’, fraction: { numerator: 1, denominator: 3 } }
// 示例3:复杂小数 const result3 = decimalToFraction(0.142857) // { display: ‘1/7’, fraction: { numerator: 1, denominator: 7 } }
// 示例4:负数 const result4 = decimalToFraction(-0.75) // { display: ‘-3/4’, fraction: { numerator: 3, denominator: 4, isNegative: true } }
// 示例5:无理数近似(√2 ≈ 1.41421356) const result5 = decimalToFraction(1.41421356, 1e-6) // { display: ‘1393/985’, … } 这是√2的一个有理逼近
总结
使用ArkTS实现小数转分数的关键点:
- 连分数算法是最优的小数转分数方法,能找到最简分数
- 需要设置合适的精度容差,太小可能导致分母过大
- 对于常见分数(1/2, 1/3等)可以直接识别,提升用户体验
- 必须处理负数、整数、无穷大等特殊情况
- 约分使用欧几里得算法求最大公约数
- 项目链接:https://gitee.com/solgull/math-fbox
更多关于HarmonyOS 鸿蒙Next数学类上架项目解析8-小数转分数算法的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
能不能把所有问题发一个帖子啊 发了好几个了
鸿蒙Next数学类库中,小数转分数算法主要基于有理数逼近原理。核心函数通过计算分子分母的最大公约数实现约分,将有限小数或循环小数转换为最简分数形式。算法内部处理了浮点数精度问题,确保转换准确性。开发者调用相关API时,需传入目标小数,库函数将返回对应的分数对象。该功能封装在系统数学模块中,无需额外实现底层逻辑。
在HarmonyOS Next中,使用ArkTS实现小数转分数算法,可以结合数学原理和ArkTS的数值处理能力。以下是一个核心实现方案:
1. 核心算法原理 对于有限小数,转换基于:小数位数n确定分母为10ⁿ,分子为整数部分×10ⁿ+小数部分整数化,最后约分。对于需要精度容差的无限循环小数近似,通常采用连分数法或基于容差的最大分母限制法。
2. ArkTS实现示例
// 最大迭代次数和精度容差控制
const MAX_ITERATIONS = 20;
const DEFAULT_TOLERANCE = 1.0e-6;
function decimalToFraction(decimal: number, tolerance: number = DEFAULT_TOLERANCE): [number, number] {
// 处理整数和负数
if (Number.isInteger(decimal)) {
return [decimal, 1];
}
const isNegative = decimal < 0;
let num = Math.abs(decimal);
// 处理精度容差下的近似
let numerator = 1;
let denominator = 0;
let prevNumerator = 0;
let prevDenominator = 1;
for (let i = 0; i < MAX_ITERATIONS; i++) {
const integerPart = Math.floor(num);
const tempNumerator = numerator;
const tempDenominator = denominator;
numerator = integerPart * numerator + prevNumerator;
denominator = integerPart * denominator + prevDenominator;
prevNumerator = tempNumerator;
prevDenominator = tempDenominator;
const approx = numerator / denominator;
if (Math.abs(approx - Math.abs(decimal)) <= tolerance) {
break;
}
if (num - integerPart <= tolerance) {
break;
}
num = 1 / (num - integerPart);
}
// 约分到最简
const gcdValue = gcd(numerator, denominator);
let resultNumerator = numerator / gcdValue;
let resultDenominator = denominator / gcdValue;
// 恢复负数
if (isNegative) {
resultNumerator = -resultNumerator;
}
return [resultNumerator, resultDenominator];
}
// 辅助函数:计算最大公约数用于约分
function gcd(a: number, b: number): number {
a = Math.floor(Math.abs(a));
b = Math.floor(Math.abs(b));
while (b !== 0) {
const temp = b;
b = a % b;
a = temp;
}
return a;
}
3. 关键点解析
- 有限小数:直接通过小数位数确定分母,结合
gcd函数约分。 - 精度容差:通过
tolerance参数控制近似程度,循环中判断当前近似值与原始值的误差。 - 特殊情况:
- 整数直接返回分母为1。
- 负数通过标志位处理,最终结果恢复符号。
- 循环小数通过迭代连分数逼近,在容差范围内停止。
4. 使用示例
// 有限小数
console.log(decimalToFraction(0.75)); // 输出: [3, 4]
// 设置容差
console.log(decimalToFraction(0.333333, 1e-4)); // 近似为 1/3
// 负数
console.log(decimalToFraction(-2.5)); // 输出: [-5, 2]
此实现平衡了精确度和性能,通过容差控制适合多数数学计算场景。对于极高精度需求,可调整MAX_ITERATIONS和tolerance参数。

