如何将Realm中的日期列表传递到图表X轴并解决时间戳转float精度丢失问题
精度丢失原因
Float 是单精度浮点类型,尾数部分仅有23位,最多只能精确表示不超过 2^24 = 16777216 的整数值。你转换得到的时间数值远大于该阈值,转为Float时必然出现整数部分四舍五入的精度丢失问题。
可行解决方案
- 方案1:使用相对时间偏移计算X值
先提取数据集内最早的时间戳作为基准值,所有时间戳先减去基准值得到相对偏移量后,再转换为天数/秒数作为X轴值。偏移后的数值范围极小,不会超出Float的精确表示范围,后续X轴渲染时再把基准值加回即可还原为真实日期。
修正代码示例:val list = ArrayList<Entry>() // 先取数据集最小时间作为基准 val baseTime = listData.filter { it.createdAt != null } .minOf { it.createdAt!!.time } for (item: Data in listData) { if (item.createdAt != null && item.pulse != null) { val offsetMilli = item.createdAt.time - baseTime // 相对偏移转天数,数值很小不会丢精度 val x = (offsetMilli / 86400000f) // 86400000 = 24*60*60*1000 val y = item.pulse.toFloat() list.add(Entry(x, y)) } } return list - 方案2:用索引作为X值,自定义X轴格式化器
这是图表类开发最常用的避坑方案,完全规避时间数值转Float的精度问题。直接用数据下标作为X轴值,同时单独存储每个下标对应的原始时间戳,X轴渲染时根据下标取对应的时间戳格式化展示即可。
修正代码示例(以MPAndroidChart为例):val list = ArrayList<Entry>() // 存储每个下标对应的原始时间戳 val dateRefList = mutableListOf<Long>() for ((index, item) in listData.withIndex()) { if (item.createdAt != null && item.pulse != null) { val x = index.toFloat() val y = item.pulse.toFloat() list.add(Entry(x, y)) dateRefList.add(item.createdAt.time) } } // 给X轴设置自定义格式化器 chart.xAxis.valueFormatter = object : ValueFormatter() { private val dateFormat = SimpleDateFormat("MM/dd", Locale.CHINA) override fun getAxisLabel(value: Float, axis: AxisBase?): String { val pos = value.toInt() if (pos < 0 || pos >= dateRefList.size) return "" return dateFormat.format(Date(dateRefList[pos])) } } return list - 方案3:使用支持Double类型的图表Entry
如果你所用的图表库支持Double类型作为X轴数值,可直接替换原有Entry实现,时间数值直接转Double无需转Float,从根源避免单精度浮点的精度问题。
内容的提问来源于stack exchange,提问作者Ritesh Kumar sinha
相关产品推荐
相关产品推荐

