Browse Source

Fix issues working with large float values.

Large float values were being truncated to scientific notation when
being passed into PDO. This was caused by PHP's implicit float->string
behavior that loses precision. Formatting into %F will retain all the
necessary precision.

Refs #9424
Mark Story 9 years ago
parent
commit
49dc283c4e

+ 3 - 3
src/Database/Type/FloatType.php

@@ -65,7 +65,7 @@ class FloatType extends Type implements TypeInterface
      *
      * @param string|resource $value The value to convert.
      * @param \Cake\Database\Driver $driver The driver instance to convert with.
-     * @return string|resource
+     * @return string|null
      */
     public function toDatabase($value, Driver $driver)
     {
@@ -73,10 +73,10 @@ class FloatType extends Type implements TypeInterface
             return null;
         }
         if (is_array($value)) {
-            return 1;
+            return '1';
         }
 
-        return (float)$value;
+        return sprintf('%F', (float)$value);
     }
 
     /**

+ 39 - 0
tests/Fixture/DatatypesFixture.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @since         3.3.4
+ * @license       http://www.opensource.org/licenses/mit-license.php MIT License
+ */
+namespace Cake\Test\Fixture;
+
+use Cake\TestSuite\Fixture\TestFixture;
+
+/**
+ * Fixture for testing decimal, float and bigint types
+ */
+class DatatypesFixture extends TestFixture
+{
+
+    /**
+     * @var array
+     */
+    public $fields = [
+        'id' => ['type' => 'biginteger'],
+        'cost' => ['type' => 'decimal', 'length' => 20, 'precision' => 0, 'null' => true],
+        'floaty' => ['type' => 'float', 'null' => true],
+        '_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id']]]
+    ];
+
+    /**
+     * @var array
+     */
+    public $records = [];
+}

+ 4 - 4
tests/TestCase/Database/Type/FloatTypeTest.php

@@ -90,16 +90,16 @@ class FloatTypeTest extends TestCase
         $this->assertNull($result);
 
         $result = $this->type->toDatabase('some data', $this->driver);
-        $this->assertSame(0.0, $result);
+        $this->assertSame('0.000000', $result);
 
         $result = $this->type->toDatabase(2, $this->driver);
-        $this->assertSame(2.0, $result);
+        $this->assertSame('2.000000', $result);
 
         $result = $this->type->toDatabase('2.51', $this->driver);
-        $this->assertSame(2.51, $result);
+        $this->assertSame('2.510000', $result);
 
         $result = $this->type->toDatabase(['3', '4'], $this->driver);
-        $this->assertSame(1, $result);
+        $this->assertSame('1', $result);
     }
 
     /**

+ 23 - 0
tests/TestCase/ORM/QueryTest.php

@@ -42,6 +42,7 @@ class QueryTest extends TestCase
         'core.articles_tags',
         'core.authors',
         'core.comments',
+        'core.datatypes',
         'core.posts',
         'core.tags'
     ];
@@ -2913,6 +2914,28 @@ class QueryTest extends TestCase
         $this->assertEquals(2, $result->_matchingData['tags']->id);
     }
 
+
+    /**
+     * Tests that it is possible to find large numeric values.
+     *
+     * @return void
+     */
+    public function testSelectLargeNumbers()
+    {
+        $this->loadFixtures('Datatypes');
+
+        $big = '1234567890123456789';
+        $table = TableRegistry::get('Datatypes');
+        $entity = $table->newEntity([
+            'cost' => $big,
+        ]);
+        $table->save($entity);
+        $out = $table->find()->where([
+            'cost' => $big
+        ])->first();
+        $this->assertSame(sprintf('%F', $big), sprintf('%F', $out->cost));
+    }
+
     /**
      * Tests that select() can be called with Table and Association
      * instance