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
    


如果你真的想使用DoubleFloat,你可以像这样修改。

【讨论】:

以上是关于PostgreSQL JDBC 驱动程序舍入双倍值的主要内容,如果未能解决你的问题,请参考以下文章

SQLException: 找不到适合 jdbc:postgresql 的驱动程序

PostgreSQL 9 JDBC 驱动程序为存储过程返回不正确的元数据

Postgresql JDBC 驱动程序导致 NoClassDefFoundError

Android上的Postgresql JDBC连接错误

带有 PostgreSQL 和 Pentaho 的 JDBC 驱动程序

我无法在我的 macbook air m1 上安装 PostgreSQL 的 JDBC 驱动程序