【问题标题】:PostgreSQL JDBC Driver rounds crops double valuePostgreSQL JDBC 驱动程序舍入双倍值
【发布时间】: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 已经指出的那样。

标签: java postgresql jdbc


【解决方案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,你可以像这样修改。

【讨论】:

    猜你喜欢
    • 2013-10-25
    • 2012-12-01
    • 1970-01-01
    • 2014-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    相关资源
    最近更新 更多