【Android】SharedPreferences存储中没有 Double 类型数据存储的解决方式

项目需求

存储定位数据,需要保存到小数点后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 位),使得转换过程既高效又简便。

相关推荐
i道i12 分钟前
MySQL win安装 和 pymysql使用示例
数据库·mysql
小怪兽ysl12 分钟前
【PostgreSQL使用pg_filedump工具解析数据文件以恢复数据】
数据库·postgresql
wqq_99225027743 分钟前
springboot基于微信小程序的食堂预约点餐系统
数据库·微信小程序·小程序
爱上口袋的天空44 分钟前
09 - Clickhouse的SQL操作
数据库·sql·clickhouse
长亭外的少年2 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
聂 可 以2 小时前
Windows环境安装MongoDB
数据库·mongodb
web前端神器2 小时前
mongodb多表查询,五个表查询
数据库·mongodb
门牙咬脆骨2 小时前
【Redis】redis缓存击穿,缓存雪崩,缓存穿透
数据库·redis·缓存
门牙咬脆骨2 小时前
【Redis】GEO数据结构
数据库·redis·缓存
wusong9993 小时前
mongoDB回顾笔记(一)
数据库·笔记·mongodb