2017-09-15 10 views
0

私は、浮動小数点値1223473をMySQLの浮動小数点列に挿入し、戻すと値が1223470に変わります。ここでMySQLが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クライアントを使用して見られています。

入力値が出力と同じではない理由を説明できませんでした。さらに、なぜオペレーションを適用することで正しい価値が得られるのでしょうか?他の値を指定しても、私は同じ動作をします。たとえば、122473の代わりに1223472を使用すると、出力値は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) 

なぜ値が変更されますか?

答えて

1

データタイプとしてfloatを使用しています。 1223473.0を表すのに十分な精度がありません。浮動小数点数は、MySQL documentationごとに「プラットフォーム依存または実装依存」の影響を受けます。

列のデータ型をdouble(またはdecimal)に変更すると、問題が解決するはずです。確認のためthis SQLFiddleを参照してください。

floatまたはdoubleの値で実行すると、集計関数はSUM, AVG, return a doubleのようになります。ドキュメントからは明らかではありませんが、MINMAX+ 0または* 1を使用した場合も、「結果の値を保持できる」データ型になることが強く疑われます。

浮動小数点演算は、tolerance(つまりしきい値)内で実行する必要があります。これは、MySQLのマニュアルでも指定されています。

+0

私は、Java floatがこの数値を表現するのに十分であっても、MySQLのfloatの精度が不十分だと考えます。 –

+0

私が理解していないのは、その上に操作を適用するときに初期値が戻ってくる理由です。私はこれをより良く示すために私の質問を編集しました。 –

+0

追加情報を追加しました。 SUMの場合、AVG:これらの関数はdoubleを返します。 「結果を保持できる」データが他のものと一緒に返されることを強く疑う。 doubleを 'float(8,1)'に変更すると、問題が解決されます。結論:浮動小数点型を使用するときは、値を保持するのに十分な大きさのデータ型を持っていることを確認し、それ以外の場合は 'decimal' – jhenderson2099

関連する問題