PreparedStatement、浮点值和 postgresql

Luc*_*rum 4 java database postgresql jdbc prepared-statement

为什么使用 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();
Run Code Online (Sandbox Code Playgroud)

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

这是来自 pgadmin4 的输出。

pgadmin4 和保存的数据

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

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.2.8</version>
    </dependency>
Run Code Online (Sandbox Code Playgroud)

我用于创建表的脚本。

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;
Run Code Online (Sandbox Code Playgroud)

我正在使用 Java 11。

And*_*eas 5

Afloat具有6 到 9 位有效十进制数字精度
Adouble具有15 到 17 位有效十进制数字精度
-50.505050是 8 位数字,这超出了 afloat可以处理的限制。

如果您打印它,您可以亲眼看到它。

System.out.printf("%.6f%n", -50.505050f);
System.out.println(new BigDecimal(-50.505050f));
System.out.println(new BigDecimal(-50.505050d));
Run Code Online (Sandbox Code Playgroud)

输出

-50.505051
-50.5050506591796875
-50.5050499999999971123543218709528446197509765625
Run Code Online (Sandbox Code Playgroud)

数据库类型numeric(9,6)应作为BigDecimal. 或者,double可以使用 a,但float没有存储此类值所需的有效十进制数字精度。

除非您受到Java 内存的严重限制(极其罕见),否则永远不要使用float. 仅保证 6 位精度,它根本没有用/可靠,所以不要使用它。

  • @LucianoBrum 因为它是一个 **8** 位数字。请注意,6-9 *有效*小数位精度是总位数,而不仅仅是小数点右侧的小数位。 (3认同)