PreparedStatement、浮点值和 postgresql

PreparedStatement, float values and postgresql

为什么使用 setFloat 的 PreparedStatement 给出的结果与直接 INSERT 不同(精度较低)?

下面的代码显示了问题:

    Connection conn = createConnection();

/* here works as expected */
    String sql = "INSERT INTO teste values (1, -50.505050)";
    PreparedStatement ps = conn.prepareStatement(sql);
    ps.executeUpdate();

/* below, all values were saved with less precision */
    sql = "INSERT INTO teste values (?, ?)";
    ps = conn.prepareStatement(sql);

    ps.setInt(1, 2);
    ps.setFloat(2, -50.505050f);
    ps.executeUpdate();

    ps.setInt(1, 3);
    ps.setFloat(2, (float) -50.505050);
    ps.executeUpdate();

    ps.setInt(1, 4);
    ps.setFloat(2, Float.valueOf(-50.505050f));
    ps.executeUpdate();

    ps.setInt(1, 5);
    ps.setFloat(2, Float.valueOf("-50.505050"));
    ps.executeUpdate();

// with double works ok, but how? //
    ps.setInt(1, 6);
    ps.setDouble(2, -50.505050);
    ps.executeUpdate();

    ps.close();
    conn.close();

数据库是 PostgreSQL,有两列,id (bigint) 和 valor (numeric (9,6))。

这是 pgadmin4 的输出。

pom.xml 中唯一相关的依赖如下:

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.2.8</version>
    </dependency>

我用于创建 table 的脚本。

CREATE TABLE public.teste
(
    id bigint NOT NULL,
    valor numeric(9,6),
    CONSTRAINT teste_pkey PRIMARY KEY (id)
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

ALTER TABLE public.teste
    OWNER to postgres;

我正在使用 Java11.

A floatfrom 6 to 9 significant decimal digits precision.
doublefrom 15 to 17 significant decimal digits precision.
-50.505050 是 8 位数字,这超出了 float 可以处理的限制。

如果你打印出来,你可以自己看看。

System.out.printf("%.6f%n", -50.505050f);
System.out.println(new BigDecimal(-50.505050f));
System.out.println(new BigDecimal(-50.505050d));

输出

-50.505051
-50.5050506591796875
-50.5050499999999971123543218709528446197509765625

数据库类型 numeric(9,6) 应作为 BigDecimal 存储在 Java 中。或者,可以使用 double,但 float 没有存储此类值所需的有效小数位精度。

除非您 严重 受 Java 内存限制(极为罕见),否则永远不要使用 float。只保证6位精度,根本就不是useful/reliable,所以不要用