我遇到的问题是我想在postgres 9.6数据库上建立一个平均超过6个值,该数据库应该导致结果5.0但是将在我的java应用程序4.99999999中。
创建sql表和值:
CREATE TABLE mytesttable(
value double precision
);
INSERT INTO mytesttable (value)
VALUES (5),
(5.1),
(5.3),
(5),
(5.4),
(4.2)
;
现在,如果您在pgAdminIII中处理以下 SELECT语句,它将在gui中返回正确的5:
SELECT AVG(value) AS value_avg FROM mytesttable;
但在Java中它将是4.9999 ....我使用以下 postgres jdbc驱动程序:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1212</version>
</dependency>
要从数据库中获得平均值,我创建一个会话并执行该语句,如您在我的java代码中所见:
Class.forName(driver);
Connection connection = DriverManager.getConnection(host, user, password);
String sql = "SELECT AVG(value) AS value_avg FROM mytesttable";
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
Double doubleValue = rs.getDouble("value_avg");
System.out.println("own table - double-value: "+doubleValue);
String doubleString = rs.getString("value_avg");
System.out.println("own table - string-value: "+doubleString);
BigDecimal bigDecimal = rs.getBigDecimal("value_avg");
System.out.println("own table - bigdecimal-value: "+bigDecimal);
}
java的控制台中的 结果是:
自己的表 - 双值:4.999999999999999
自己的表 - 字符串值:4.9999999999999991
自己的表 - bigdecimal-value:4.9999999999999991
正如您所看到的,我还尝试将值检索为BigDecimal以及String - 不起作用。 有人知道如何避免这种浮点错误吗?
答案 0 :(得分:2)
如果该值仅用于表示,您可以考虑在Java端显示较少的数字,而不是更改SQL语句。
double doubleValue = rs.getDouble("value_avg");
System.out.format("own table - double-value: %.4f", doubleValue);
// should print `5.0000`.
浮点错误源自PostgreSQL,因此没有必要提高Java端的精度。虽然你可能会看到&#34; 5&#34;从SELECT语句出来,事实是你的PostgreSQL客户端没有显示结果的所有数字。
您可以将数字转换为numeric
type以获得定点运算。
类型
numeric
可以存储具有大量数字的数字并完全执行计算。特别推荐用于存储需要准确性的货币金额和其他数量。但是,与整数类型或下一节中描述的浮点类型相比,numeric
值的算术运算速度非常慢。
下面的前3列表明PostgreSQL方面的平均值确实不精确。最后3列显示使用定点计算而不是浮点计算(您可能希望更改numeric(4, 2)
以提高精度)。
SELECT
avg(n), -- 5
avg(n) = 5, -- false
avg(n) - floor(avg(n)), -- 0.999999999999999
avg(n :: numeric(4, 2)), -- 5
avg(n :: numeric(4, 2)) = 5, -- true
avg(n :: numeric(4, 2)) - floor(avg(n :: numeric(4, 2))) -- 0
FROM (VALUES
(5 :: double precision),
(5.1 :: double precision),
(5.3 :: double precision),
(5 :: double precision),
(5.4 :: double precision),
(4.2 :: double precision)
) t(n)
答案 1 :(得分:2)
浮点值的所有数值运算都是不精确的。
你没有注意到,通常是PostgreSQL在将real
或double precision
值转换为文本时,会在一定数量的数字之后进行舍入,这样才能得到结果在所有平台上都是一样的。
这由参数extra_float_digits
控制。如果将该参数从其默认值0(最大值为3)增加,您将获得更多数字,这将使文本表示更准确,但会显示舍入错误:
SET extra_float_digits=3;
SELECT AVG(value) AS value_avg FROM mytesttable;
value_avg
---------------------
4.99999999999999911
(1 row)
或者更令人惊讶的是:
SELECT 0.3::double precision;
float8
----------------------
0.299999999999999989
(1 row)
现在,PostgreSQL JDBC驱动程序将extra_float_digits
设置为2或3,以避免丢失任何精度,这可能会导致您观察到的效果。
如果您不在乎这些额外的数字并且宁愿有一个很好的圆值,请将extra_float_digits
更改回0:
conn.createStatement().execute("SET extra_float_digits=0");