Orange-OpenSource / YACassandraPDO

Cassandra PDO Driver fork
Apache License 2.0
85 stars 32 forks source link

Float returned by microtime is returned broken #60

Open martin-marinov-securax opened 10 years ago

martin-marinov-securax commented 10 years ago

Issue: When I generate a float number using microtime and save it into a float cassandra field, then fetch it, the value is not the original value, returned by microtime. It does not even appear to be a float number, but rather an integer number stored in a float variable.

In the example below: float returned by microtime: 1406709742.7362

float returned by the PDO: 1406709760

My script: $f = microtime(true); var_dump($f); $stmt = $db->prepare("INSERT INTO test (id, value) VALUES (1, :f)"); $stmt->bindValue(':f', $f, \PDO::CASSANDRA_FLOAT); $stmt->execute();

    $stmt2 = $db->prepare("SELECT * FROM test");
    $stmt2->execute();

    var_dump($stmt2->fetchAll(\PDO::FETCH_ASSOC));

Output: float(1406709742.7362) array(1) { [0]=> array(2) { ["id"]=> int(1) ["value"]=> float(1406709760) } }

Table test: CREATE TABLE test ( id int, value float, PRIMARY KEY ((id)) )

LexLythius commented 10 years ago

This is actually not a driver bug but a limitation of the CQL float data type, which is a signed, single precision (32-bit) floating point number. Big floats like 1407827956.66896796 will suffer when squeezed into a signed 32-bit float, since floats have to reserve a few bits to handle the position of the floating comma (even if in this case, where all decimal digits have been discarded). The net result is: you lose precision, big time.

As an example, I created this table and inserted a few numbers in it:

CREATE TABLE int_numbers (kint int PRIMARY KEY, vbigint bigint, vfloat float, vdouble double);
INSERT INTO int_numbers (kint, vfloat) VALUES (31, 1407827956.669);
INSERT INTO int_numbers (kint, vdouble) VALUES (21, 1407827956.669);

Check out the results (I edited CQLSH's float_precision setting to show more digits):

> select * from int_numbers;

 kint | vbigint | vdouble               | vfloat
------+---------+-----------------------+------------
   21 |    null | 1407827956.6689999104 |       null
   31 |    null |                  null | 1407827968

(2 rows)

As you can see, even the big 64-big double type isn't all that accurate. Bottomline: if you need exact comparisons, use integer types like bigint. If you don't need exact comparison, prefer double to float for anything but very small ranges of values. See if changing your column to double solves your issue and let us know.