An example may show the problem. Watch the floats!

db_query("INSERT chatblock (message, username, timestamp) VALUES (%s, %s, %f)", 'test','user', 1179230890.96152); 

The resulting database row will contain this microtime timestamp for only 2 decimals precision.
I have found out that preg_replace_* functions of PHP 4 are truncating floating numbers (like echo does).

Try it yourself.

$a=1179229804.75932400; 
var_dump( preg_replace("/sg_to_be_replaced/", (float)$a, "sg_to_be_replaced") ); 

Yields:
string(13) "1179229804.76"

$a=1179229804.75932400; 
var_dump( preg_replace("/sg_to_be_replaced/", sprintf("%f", $a), "sg_to_be_replaced") ); 

Results:
string(17) "1179229804.759324"

I have modified my include/database.inc so _db_query_callback replaces floats with a sprintf string

    case '%f':
      return sprintf("%f", array_shift($args));
#      return (float) array_shift($args);
CommentFileSizeAuthor
#3 database_11.patch554 byteskaerast
#1 database_10.patch553 byteskaerast

Comments

kaerast’s picture

Version: 5.1 » 6.x-dev
Status: Active » Needs review
StatusFileSize
new553 bytes

This is a problem in Head as well and will probably want fixing there first. Attached is a patch against Drupal 6, but it should apply for Drupal 5 as well.

Is there any reason not to use sprintf here?

ChrisKennedy’s picture

Status: Needs review » Needs work

You need a space after the comma, and you might as well use single quotes.

kaerast’s picture

Status: Needs work » Needs review
StatusFileSize
new554 bytes

Re-written the patch according to ChrisKennedy's comment.

dries’s picture

I tried searching the PHP documentation to confirm this -- could you find an official statement about this behavior. It sounds very weird.

An alternative to printf, might be to use a number formatter.

Either way, this needs to be documented in the code. People are bound to frown at that construct and might wonder why we don't cast that string to a float.

But honestly, I'm not convinced that this is a bug. It means that the calling function is passing in a string, while it really should pass in a valid float. The loss of precision might not happen when you pass in a real float, rather than a string.

I'd like to see us investigate this some more ...

ainigma32’s picture

Status: Needs review » Postponed (maintainer needs more info)

I tried to reproduce the examples on Apache 1.3 with PHP 4.3.5 (minimum required for Drupal) and it looks like this has something to do with the precision ini-setting. The following code illustrates this:

$a=1179229804.75932400;
$b='1179229804.75932400';

dump($a);
dump($b);

ini_set('precision', '16');

dump($a);
dump($b);

function dump($var) {
  var_dump($var);
  echo '<br />';
  var_dump( preg_replace("/sg_to_be_replaced/", (float)$var, "sg_to_be_replaced") ); 
  echo '<br />';
  var_dump( preg_replace("/sg_to_be_replaced/", sprintf("%f", $var), "sg_to_be_replaced") ); 
  echo '<hr /><br />';
}

It looks like the preg_replace function converts the float to a string using the precision ini-setting while sprintf seems to ignore the setting and only drops the (unnecessary) last two zeros.

So I suppose the question is now: should we change the precision ini-setting, do we start using sprintf or is there another solution out there?

You PHP gurus out there, feel free to jump in anytime ;-)

Also tried this little script on Apache 2.2 and PHP 5.2.6 and it produces the same result.

dpearcefl’s picture

Status: Postponed (maintainer needs more info) » Closed (won't fix)

Closing this issue because of a lack of activity.

Gabriel R.’s picture

@ubul : you will have to make sure the value is float, just convert it, like: (float) 12.34 or (float) $myvalue