PostgreSQL JDBC 驱动程序舍入双倍值
Posted
技术标签:
【中文标题】PostgreSQL JDBC 驱动程序舍入双倍值【英文标题】:PostgreSQL JDBC Driver rounds crops double value 【发布时间】:2015-01-09 08:38:59 【问题描述】:我将 PostgreSql 9.3 与 postgresql-9.3-1101 JDBC 驱动程序一起使用。
PreparedStatements
有一个非常奇怪的行为。
这是一个例子: 首先我创建一个测试表:
CREATE TABLE double_test
(
id numeric(18) NOT NULL
);
之后我运行以下代码:
DecimalFormat df = new DecimalFormat("0");
try
conn = BeanUtils.getBean("dataSource", DataSource.class).getConnection();
conn.setAutoCommit(false);
conn.createStatement().execute("DELETE from double_test ");
conn.commit();
PreparedStatement stmt = conn.prepareStatement("INSERT INTO double_test (id )VALUES (?)");
double input = 1234567890123456d;
System.out.println("Input:" + df.format(input));
stmt.setDouble(1, input);
stmt.execute();
conn.commit();
ResultSet rs = conn.createStatement().executeQuery("SELECT id FROM double_test ");
rs.next();
System.out.println("Output:" + df.format(rs.getDouble(1)));
rs.close();
catch (SQLException e1)
throw new RuntimeException(e1);
finally
try
conn.close();
catch (SQLException e)
throw new RuntimeException(e);
代码的输出将是:
Input: 1234567890123456
Output:1234567890123460
这让我很困惑,在 15 位之后,Postgres JDBC 驱动程序开始四舍五入。插入的数据已损坏,恕不另行通知!
我在这里错过了什么吗?我的代码有错误吗?
或者这是驱动程序中的错误?如果是这样,我真的很想知道还没有人注意到这一点。
也许使用BigDecimal
可以防止数据损坏,但这只是一种解决方法。
【问题讨论】:
使用BigDecimal
不是一种“解决方法”,它是处理numeric
列的正确方法。 double
是一个近似数据类型:floating-point-gui.de
@a_horse_with_no_name 但他/她将一个 double
放入,并取出另一个。
@a_horse_with_no_name 肯定都是浮点值的近似值,但我们不是在讨论小数点的四舍五入。它只是削减 15. 数字之后的值并用零填充它。只需在示例中添加数字,您就会看到。
@Orism 1234567890123456
只是0.1234567890123456E16
的简写,浮点数将在其有效部分四舍五入,而PostgreSQL 的numeric
不会对其值进行四舍五入。这就是为什么您应该选择客户端的表示,它不适用四舍五入。
事实是,如果您希望它以一致的方式正确执行,您必须使用BigDecimal
,正如@a_horse_with_no_name 已经指出的那样。
【参考方案1】:
有两个问题。
首先来自Double
类:
System.out.println(0.1 + 0.2); //output 0.30000000000000004
所以通常不要使用Double
(也可以是Float
)。
其次是jdbc驱动,尝试用与org.postgresql.jdbc.PgPreparedStatement
相关的类的toString方法保存数据库。
检查一下:
System.out.println(new BigDecimal("0.00000072")); // print 7.2E-7
它是相等的,但是当 Jdbc 驱动程序用这个保存时,一些精度损坏,所以我替换
public void setBigDecimal(@Positive int parameterIndex, @Nullable BigDecimal x)
throws SQLException
setNumber(parameterIndex, x); // It is using toString method
用这个:
public void setBigDecimal(@Positive int parameterIndex, @Nullable BigDecimal x)
throws SQLException
checkClosed();
if (x == null)
setNull(parameterIndex, Types.DECIMAL);
else
bindLiteral(parameterIndex, x.toPlainString(), Oid.NUMERIC); //now using toPlainString
如果你真的想使用Double
或Float
,你可以像这样修改。
【讨论】:
以上是关于PostgreSQL JDBC 驱动程序舍入双倍值的主要内容,如果未能解决你的问题,请参考以下文章
SQLException: 找不到适合 jdbc:postgresql 的驱动程序
PostgreSQL 9 JDBC 驱动程序为存储过程返回不正确的元数据
Postgresql JDBC 驱动程序导致 NoClassDefFoundError