项目需求
存储定位数据,需要保存到小数点后10位数据。
需求分析
项目需求看起来很简单,其实实现起来也不难,我们直接使用SharedPreferences 存储一下就好了,反正也没其他要求。
好了,直接使用SharedPreferences 存储一下double数据就行了,
但是,SharedPreferences 不能存储double数据????
。。。。。。
是的,SharedPreferences 就是不能存储double类型的数据,虽然double类型的数据也是基本类型数据。。。
SharedPreferences中存储的数据是以key/value键值对的形式保存在XML文件中,这些数据具有一定的类型限制。具体来说,SharedPreferences能够直接存储的数据类型包括:
- String:字符串类型,用于存储文本信息。
- int:整型,用于存储整数数据。
- float:浮点型,用于存储小数数据。
- long:长整型,用于存储比int类型范围更大的整数数据。
- boolean:布尔型,用于存储真或假两种状态。
- StringSet:字符串集合类型,用于存储一组字符串。
那我们的需求是要保存的数据有10位小数的,但是使用float存储的话这个精度也打不到我们的需求,float 类型大约能精确表示 6-9 位有效数字,其中小数部分通常不超过 7 位;而 double 类型大约能精确表示 15-17 位有效数字,其中小数部分通常不超过 15 位。
所以我们要是满足需求的话是要使用double的,那怎么解决这个问题呢。
解决方法
解决方法一
将 double 转换为 String
java
// 存储 double
SharedPreferences sharedPreferences = context.getSharedPreferences("myPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
double myDouble = 12.34;
editor.putString("myDoubleKey", String.valueOf(myDouble));
editor.apply();
使用的时候将一个 String 类型的带小数的数据转换为 double 类型,并保留10位小数。在这个过程中,可以使用 BigDecimal 来处理精度问题。
java
public static double convertStringToDouble(String str) {
try {
// 将字符串转换为 BigDecimal
BigDecimal bigDecimal = new BigDecimal(str);
// 设置精度并进行舍入
bigDecimal = bigDecimal.setScale(10, RoundingMode.HALF_UP);
// 转换为 double
return bigDecimal.doubleValue();
} catch (NumberFormatException e) {
// 处理转换异常
System.err.println("Invalid number format: " + str);
return 0.0; // 或者根据需要返回其他值
}
}
解决方法二
将 double 值转换为 long
这看上去第一眼就懵逼了,不是,这个long能存小数吗?那肯定不能啊。
long 类型本身是整数,不能直接存储小数。但是,使用 long 来存储 double 值的关键在于如何处理它们的二进制表示。
首先我们要知道:
-
double 在内存中以 IEEE 754 格式表示,它包含了符号位、指数部分和尾数部分。
-
这个表示可以被转换为一个 64 位的整数(long),通过 Double.doubleToRawLongBits() 方法。
-
调用 Double.doubleToRawLongBits(value) 时,实际上是将 double 的内存表示(即二进制数)转换为一个 long 类型的数字。这并不是说 long 存储了小数,而是 long 存储了 double 的二进制表示。
-
这个 long 数字可以被完整地还原为原始的 double 值。
浮点数的内存表示
double 类型在 Java 中使用 IEEE 754 标准进行表示。它包含:
- 1 位符号位:表示正数或负数。
- 11 位指数:用来表示数值的范围。
- 52 位尾数(也称为有效数字):用于表示具体的数值。
这样,double 值的存储是通过这些位来表示其具体的数值特性。整个 double 在内存中占用 64 位(即 8 字节)。
使用 long 存储 double
由于 double 是 64 位的,而 long 也是 64 位的,可以利用这种大小的相同来存储 double 的原始位表示。具体流程如下:
存储过程
- 转换:
当调用 Double.doubleToRawLongBits(value) 时,Java 将 double 值的 64 位二进制表示转换为一个 long 类型的数字。
这个 long 数字实际上是 double 值在内存中的"快照",保留了所有的位信息。
- 存储:
将得到的 long 数字存储到 SharedPreferences 中。
读取过程
- 获取 long 值:
从 SharedPreferences 中读取之前存储的 long 值。
- 还原:
使用 Double.longBitsToDouble(longBits) 方法将这个 long 值转换回原来的 double 值。
在这个过程中,Java 会重新解析这个 long 的位信息,将它解读为 double 的各个部分(符号位、指数、尾数),并计算出原始的浮点数值。
所以,假设我们有一个double数据3.14,使用SharedPreferences 存储这个数据时,我们要
转换 double 为 long
调用 Double.doubleToRawLongBits(value) 时,Java 会将 double 值的 64 位二进制表示转换为一个 long 类型的数字。这一过程包括以下几个步骤:
内部表示:
首先,Java 使用 IEEE 754 标准来表示 double 值。这个表示方式将 double 的数值分解为三部分:
- 符号位(1 位):决定数值是正是负。
- 指数部分(11 位):用于表示数值的范围和大小。
- 尾数(有效数字)(52 位):表示实际的数值。
生成长整型:
Double.doubleToRawLongBits(value) 方法会将这些位拼接成一个 64 位的整数(long),这个整数就是 double 在内存中的原始二进制表示。
例如,对于 3.14,这个转换会生成一个特定的 long 值,表示 3.14 在 IEEE 754 中的位模式。
存储 long 到 SharedPreferences
得到了这个 long 值,就可以将其存储在 SharedPreferences 中:
java
long bits = Double.doubleToRawLongBits(value);
sharedPreferences.edit().putLong("key", bits).apply();
获取 long 值
需要读取存储的 double 值时,首先从 SharedPreferences 中获取之前存储的 long 值:
java
long bits = sharedPreferences.getLong("key", 0);
还原 long 为 double
将这个 long 值转换回原始的 double 值,使用 Double.longBitsToDouble(longBits) 方法:
java
double value = Double.longBitsToDouble(bits);
解析过程:
在这一过程中,Java 会根据 long 中的位信息重新构造出 double 的三个组成部分:
- 符号位:如果符号位为 0,则表示正数;如果为 1,则表示负数。
- 指数部分:根据存储的位值,Java 会计算出实际的指数。
- 尾数:有效数字也会根据存储的位值重新构造出来。
这个过程会将 long 的二进制位解析成 double 需要的各个部分,然后合成最终的浮点数值。
通过以上的步骤,Java 能够有效地将 double 类型的数值以其原始二进制格式存储为 long 类型,从而在 SharedPreferences 中安全地保存和读取。这个方法不仅确保了数据的准确性,还利用了 long 和 double 相同的存储大小(64 位),使得转换过程既高效又简便。