温度单位转换App输入超4位数字结果异常的解决求助
问题分析与解决方案
问题根源
你的代码依赖Double类型处理温度数值,但Double作为浮点数,存在精度上限(有效位数约15-17位),当输入数字长度超过一定范围时,Double无法精确存储该数值,直接导致后续转换计算出现偏差。同时,你手动用times(100000.0).roundToInt().div(100000.0)处理小数的逻辑,也会额外引入精度损失。
修复方案
改用BigDecimal处理所有数值计算与转换——它支持任意精度的十进制数,能彻底规避浮点数精度问题,同时完美支持超长数字输入。以下是修改后的完整代码:
修改后的代码
import java.math.BigDecimal import java.math.RoundingMode @Composable fun TemperatureScreen( conversionModel: ConversionModel, navController: NavHostController ) { val focusRequester = remember { FocusRequester() } var number by rememberSaveable { mutableStateOf("1") } var sourceUnit by remember { mutableStateOf(TemperatureUnits.Kelvin) } var targetUnit by remember { mutableStateOf(TemperatureUnits.DegreeCelsius) } var expandedSU by remember { mutableStateOf(false) } var expandedTU by remember { mutableStateOf(false) } // 用BigDecimal安全转换输入字符串,避免非法输入崩溃 val inputValue = runCatching { BigDecimal(number) }.getOrNull() // 基于BigDecimal完成温度转换 val convertedValue = inputValue?.let { convertTemperatureValueTo(it, sourceUnit, targetUnit) } // 保留5位小数并转为普通字符串(避免科学计数法) val roundedValue = convertedValue?.setScale(5, RoundingMode.HALF_UP)?.toPlainString() // 此处保留你原有的UI实现代码(你提供的片段中未包含,故省略) } // 所有转换函数改用BigDecimal作为参数和返回值 fun convertTemperatureValueTo( value: BigDecimal, sourceUnit: TemperatureUnits, targetUnit: TemperatureUnits ): BigDecimal { return when (sourceUnit) { TemperatureUnits.Kelvin -> when (targetUnit) { TemperatureUnits.Kelvin -> value TemperatureUnits.DegreeCelsius -> kelvinToCelsius(value) TemperatureUnits.Fahrenheit -> kelvinToFahrenheit(value) TemperatureUnits.Rankine -> kelvinToRankine(value) } TemperatureUnits.DegreeCelsius -> when (targetUnit) { TemperatureUnits.Kelvin -> celsiusToKelvin(value) TemperatureUnits.DegreeCelsius -> value TemperatureUnits.Fahrenheit -> celsiusToFahrenheit(value) TemperatureUnits.Rankine -> celsiusToRankine(value) } TemperatureUnits.Fahrenheit -> when (targetUnit) { TemperatureUnits.Kelvin -> fahrenheitToKelvin(value) TemperatureUnits.DegreeCelsius -> fahrenheitToCelsius(value) TemperatureUnits.Fahrenheit -> value TemperatureUnits.Rankine -> fahrenheitToRankine(value) } TemperatureUnits.Rankine -> when (targetUnit) { TemperatureUnits.Kelvin -> rankineToKelvin(value) TemperatureUnits.DegreeCelsius -> rankineToCelsius(value) TemperatureUnits.Fahrenheit -> rankineToFahrenheit(value) TemperatureUnits.Rankine -> value } } } //---------------------------------Kelvin To Any-------------------------------------------------------------------- fun kelvinToCelsius(kelvin: BigDecimal): BigDecimal { return kelvin.subtract(BigDecimal("273.15")) } fun kelvinToFahrenheit(kelvin: BigDecimal): BigDecimal { return kelvin.subtract(BigDecimal("273.15")) .multiply(BigDecimal("9").divide(BigDecimal("5"), 10, RoundingMode.HALF_UP)) .add(BigDecimal("32")) } fun kelvinToRankine(kelvin: BigDecimal): BigDecimal { return kelvin.multiply(BigDecimal("1.8")) } //---------------------------------Celsius To Any-------------------------------------------------------------------- fun celsiusToKelvin(celsius: BigDecimal): BigDecimal { return celsius.add(BigDecimal("273.15")) } fun celsiusToFahrenheit(celsius: BigDecimal): BigDecimal { return celsius.multiply(BigDecimal("9").divide(BigDecimal("5"), 10, RoundingMode.HALF_UP)) .add(BigDecimal("32")) } fun celsiusToRankine(celsius: BigDecimal): BigDecimal { val kelvin = celsius.add(BigDecimal("273.15")) return kelvin.multiply(BigDecimal("1.8")) } //---------------------------------fahrenheit To Any-------------------------------------------------------------------- fun fahrenheitToKelvin(fahrenheit: BigDecimal): BigDecimal { return fahrenheit.subtract(BigDecimal("32")) .multiply(BigDecimal("5").divide(BigDecimal("9"), 10, RoundingMode.HALF_UP)) .add(BigDecimal("273.15")) } fun fahrenheitToCelsius(fahrenheit: BigDecimal): BigDecimal { return fahrenheit.subtract(BigDecimal("32")) .multiply(BigDecimal("5").divide(BigDecimal("9"), 10, RoundingMode.HALF_UP)) } fun fahrenheitToRankine(fahrenheit: BigDecimal): BigDecimal { return fahrenheit.add(BigDecimal("459.67")) } //---------------------------------rankine To Any-------------------------------------------------------------------- fun rankineToKelvin(rankine: BigDecimal): BigDecimal { return rankine.multiply(BigDecimal("5").divide(BigDecimal("9"), 10, RoundingMode.HALF_UP)) } fun rankineToCelsius(rankine: BigDecimal): BigDecimal { return rankine.subtract(BigDecimal("491.67")) .multiply(BigDecimal("5").divide(BigDecimal("9"), 10, RoundingMode.HALF_UP)) } fun rankineToFahrenheit(rankine: BigDecimal): BigDecimal { return rankine.subtract(BigDecimal("459.67")) } // 适配你的TemperatureUnits枚举(若枚举结构不同可自行调整) enum class TemperatureUnits { Kelvin, DegreeCelsius, Fahrenheit, Rankine }
关键修改说明
- 全链路用BigDecimal替代Double:从输入转换到计算输出全程使用
BigDecimal,彻底解决浮点数精度丢失问题,支持任意长度的数字输入。 - 精确的除法运算:涉及9/5、5/9这类非整数除法时,指定足够的计算精度(示例中设为10位)和舍入模式(
RoundingMode.HALF_UP),保证计算结果准确。 - 简化结果处理:用
setScale(5, RoundingMode.HALF_UP)直接控制小数位数,toPlainString()自动避免科学计数法,无需额外处理逻辑。 - 安全的输入转换:通过
runCatching捕获非法输入的转换异常,避免App崩溃。
额外优化建议
- 给TextField添加输入过滤器,限制仅能输入数字和小数点,减少非法输入场景。
- 可新增自定义小数位数的设置项,让用户按需调整显示精度。
内容的提问来源于stack exchange,提问作者Harsh Wavikar
相关产品推荐
相关产品推荐

