浮点数异常截图:
说明:
正常保留两位小数并正确插入的记录是通过db.execSQL(sql);方法插入的,而浮点数异常的是通过ContentValues db.insert() 方式插入的,可以发现问题出在db.insert()方法上,我又试过在put的时候直接输入类似166.88存进去又是正常的。
查看db.insert源码:
public long insertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) {
acquireReference();
try {
StringBuilder sql = new StringBuilder();
sql.append("INSERT");
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(" INTO ");
sql.append(table);
sql.append('(');
Object[] bindArgs = null;
int size = (initialValues != null && !initialValues.isEmpty())
? initialValues.size() : 0;
if (size > 0) {
bindArgs = new Object[size];
int i = 0;
for (String colName : initialValues.keySet()) {
sql.append((i > 0) ? "," : "");
sql.append(colName);
bindArgs[i++] = initialValues.get(colName);
}
sql.append(')');
sql.append(" VALUES (");
for (i = 0; i < size; i++) {
sql.append((i > 0) ? ",?" : "?");
}
} else {
sql.append(nullColumnHack + ") VALUES (NULL");
}
sql.append(')');
SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
try {
return statement.executeInsert();
} finally {
statement.close();
}
} finally {
releaseReference();
}
}
可以看到是因为 put进去的float类型数据被内部做了数据类型转换. SQLiteDatabase的db.insert()
方法会根据传入的值类型自动转换数据类型,可能会导致精度丢失的问题.
ContentValues values = new ContentValues();
//values.put("id", pay.getId());
values.put("name", pay.getName());
values.put("amount", pay.getAmount());
values.put("real_amount", pay.getReal_amount());
values.put("yh_amount", pay.getYh_amount());
如何解决这个问题:?
1,将字段类型改成String
2,使用db.execSQL(sql);方法拼接原生SQL插入
3, 在java代码中做转换,使用BigDecimal来转换保存。(麻烦)
4, 将Java中定义的金额类型由float改成double(首选)
5, 将SQLite中的金额字段类型改成Integer整数,然后按最小单位处理,比如8.56保存为856做位移(大部分人的做法)
我选择了第2种或第4种(视情况),用回最原始的拼接SQL直接执行的方式,安全靠谱简单。哈哈
db.insert()
方法会根据传入的值类型自动转换数据类型,可能会导致精度丢失的问题。在SQLite中,float
类型的数据只能存储约7位有效数字,如果插入的数据精度超过了这个范围,那么SQLite会自动将其截断或四舍五入,从而导致小数位数变长的现象。如果你需要在SQLite中存储精度较高的数据,可以考虑使用
REAL
类型或NUMERIC
类型,它们都可以存储高精度的数据。另外,为了避免精度丢失的问题,你可以将float
类型改为double
类型,这样就可以存储更多位的有效数字。
得出一个结论,java中能不用float数据类型就尽量不要用