为什么MySQL的float漂移与Java float不同?

问题描述:

我在MySQL浮动列中插入一个浮点值1223473,当我找回它时,该值更改为1223470。为什么MySQL的float漂移与Java float不同?

import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.Statement; 

public class Main { 

    public static void main(String[] args) throws Exception { 

     Connection conn = null; 
     Statement stmt = null; 
     ResultSet rs = null; 
     PreparedStatement pstmt = null; 

     try { 
      // SETUP 
      Class.forName("com.mysql.jdbc.Driver"); 
      conn = DriverManager.getConnection("jdbc:mysql://..."); 

      stmt = conn.createStatement(); 
      stmt.execute("DROP TABLE IF EXISTS ttt"); 

      stmt.execute("CREATE TABLE ttt (value FLOAT)"); 

      // INPUT VALUE 
      float input = 1.223473E6f; 
      print(" Input", input); 
      pstmt = conn.prepareStatement("INSERT INTO ttt VALUES (?)"); 
      pstmt.setFloat(1, input); 
      pstmt.execute(); 

      // OUTPUT VALUES 
      rs = stmt.executeQuery("SELECT value, " 
        + "AVG(value) as avg, MIN(value) as min, " 
        + "MAX(value) as max, value + 0 as plus, " 
        + "value * 1 as mult, " 
        + "cast(value as decimal) as valcast " 
        + "FROM ttt;"); 
      rs.next(); 
      System.out.println(); 
      print("Output", rs.getFloat("value")); 
      print(" AVG", rs.getFloat("avg")); 
      print(" MIN", rs.getFloat("min")); 
      print(" MAX", rs.getFloat("max")); 
      print(" +0", rs.getFloat("plus")); 
      print(" *1", rs.getFloat("mult")); 
      print(" CAST", rs.getFloat("valcast")); 

     } finally { 
      if (stmt != null) 
       stmt.close(); 
      if (pstmt != null) 
       pstmt.close(); 
      if (conn != null) 
       conn.close(); 
     } 

    } 

    private static void print(String name, float value) { 
     System.out.println(name + ": " + value + " (0x" 
      + Integer.toHexString(Float.floatToRawIntBits(value)) + ")"); 
    } 

这里是输出:

Input: 1223473.0 (0x49955988) 

Output: 1223470.0 (0x49955970) 
    AVG: 1223473.0 (0x49955988) 
    MIN: 1223473.0 (0x49955988) 
    MAX: 1223473.0 (0x49955988) 
    +0: 1223473.0 (0x49955988) 
    *1: 1223473.0 (0x49955988) 
    CAST: 1223473.0 (0x49955988) 

相同的行为是使用MySQL客户端看到的。

我无法解释为什么输入值与输出不相同。此外,为什么应用一项操作能够提供正确的价值?即使有其他值,我也会得到相同的行为。例如,如果我使用1223472而不是122473,则输出值将为1223470.它看起来像精确度的丢失。但为什么在其上应用操作会返回完全的输入值?它看起来像MySQL存储比它显示的更精确。

它看起来并不像有关浮点类型,人们试图把过多的数位在浮动的典型误解:

System.out.println(1223473f == 1223470f); // gives false 
System.out.println(123456789f == 123456791f); // gives true (too many digits) 

最后,我不知道该怎么想想...

mysql> create table ttt (value float); 
Query OK, 0 rows affected (0.04 sec) 

mysql> INSERT INTO ttt VALUES (1223473); 
Query OK, 1 row affected (0.00 sec) 

mysql> select * from ttt; 
+---------+ 
| value | 
+---------+ 
| 1223470 | 
+---------+ 
1 row in set (0.00 sec) 

mysql> SELECT * FROM ttt WHERE value = 1223470; 
Empty set (0.00 sec) 

mysql> SELECT * FROM ttt WHERE value = 1223473; 
+---------+ 
| value | 
+---------+ 
| 1223470 | 
+---------+ 
1 row in set (0.00 sec) 

为什么值发生变化?

您正在使用float作为数据类型。你没有足够的精度来表示1223473.0。根据MySQL documentation,浮点数也受“平台或实施依赖性”的约束。

将列上的数据类型更改为double(或decimal),这应该可以解决您的问题。请参阅this SQLFiddle进行确认。

当在floatdouble的值上执行时,聚合函数的值如SUM, AVG, return a double。从文档中不清楚,但我强烈怀疑,当使用MIN,MAX,+ 0* 1也会导致可“保留结果值”的数据类型。

您应该意识到浮点操作应该在tolerance(即阈值)内进行,也是按照MySQL文档的规定。

+0

我同意看起来MySQL float没有足够的精度,即使Java float已经足以表示这个数字。 –

+0

我不明白为什么初始值在应用操作时回来。我编辑了我的问题以更好地展示这一点。 –

+0

增加了一些额外的信息。对于SUM,AVG:这些函数返回一个double。我强烈怀疑可以“保留结果”的数据与其他数据一起返回。如果将double更改为“float(8,1)”,那么这也将解决您的问题。底线:当使用浮点类型时,确保你有一个足够大的数据类型来保存你的值,否则使用'decimal' – jhenderson2099