简体   繁体   中英

Why is MySQL float different than Java float?

I insert a float value 1223473 in a MySQL float column and the value changes to 1223470 when I get it back.

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)) + ")");
    }

Here is the output:

 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)

The same behavior is seen using the MySQL client.

I was unable to explain why the input value is not the same as the output. Moreover, why applying an operation give the right value? Even with other values I get the same behavior. For example if I use 1223472 instead of 122473, the output value will be 1223470. It looks like a lost of precision. But why applying an operation on it return exactly the input value ? It looks like MySQL stores more precision than what it shows.

It does not look like the typical misunderstanding about floating point types where people try to put too many digits in a float:

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

Finally, I don't know what to think about that...

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)

Why the value changes ?

You are using a float as a data type. You don't have enough precision to represent 1223473.0. Floating point numbers are also subject to "platform or implementation dependencies" per the MySQL documentation .

Change the data type on the column to double (or decimal ) and that should resolve your problem. See this SQLFiddle for confirmation.

Aggregate functions like SUM, AVG, return a double value when performed on values of float or double . It isn't clear from the documentation, but I strongly suspect that when that this is also the case when using MIN , MAX , + 0 or * 1 also results in a data type that can "hold the resulting value".

You should be aware floating point operations should be conducted within a tolerance (ie, threshold), also as specified by the MySQL documentation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM