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 float
有 from 6 to 9 significant decimal digits precision.
double
有 from 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,所以不要用
为什么使用 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 float
有 from 6 to 9 significant decimal digits precision.
double
有 from 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,所以不要用