PHP floats and locales

I recently had a bug report in JFFNMS that the SLA checks were failing with bizarre calculations.  Things like 300% disk drive utilization and the like.  Briefly, JFFNMS is written in PHP and checks values that come out of rrdtool and makes various comparisons like have you used more than 80% of your disk or have there been too many errors.

The logs showed strange input variables coming in, all were integers below 10.  I don’t know of many 1 or 3 kB sized disk drives. What was going on?  I ran a rrdtool fetch command on the relevant file and got output of something like 1,780000e+07 which for an 18GB drive seemed ok. Notice the comma, in this locale that’s a decimal point… hmm.

In lib/api.rrdtool.inc.php there is this line around the rrdtool_fetch area:

$value[] = ($line1[$i]=="nan")?0:(float)$line1[$i];

A quick check and I was finding that my 1,7…e+07 was coming back as 1.  We had a float conversion problem.  Or more specifically, php has a float conversion problem.  I built a small check script like the following:

setlocale(LC_NUMERIC,'pl_PL.UTF-8');
$linfo = localeconv();
$pi='3,14';
print "Decimal is "$linfo[decimal_point]". Pi is $pi and ".(float)($pi)."n";
print "Half is ".(1/2)."n";

Which gave the output of:

Decimal is “,”. Pi is 3,14 and 3

Half is 0,5

So… PHP is saying that decimal point is a comma and it uses it BUT if a string comes in with a comma, its not a decimal point. Really?? Are they serious here?  I tried various combinations and could not make it parse correctly.

The fix was made easier for me because I know rrdtool fetch only outputs values in scientific notation. That means if there is a string with a comma, then it must be a decimal point as it could never be used for a thousands mark.  By using str_replace to replace any comma with a period the code worked again and didn’t even need the locale to be set correctly, or that the locale for the shell where rrdtool is run is the same as the locale in php.

Enhanced by Zemanta

5 thoughts on “PHP floats and locales

  1. I’m not fast to jump to the defense of PHP, but it’s quite correct here. (float) is just a type-cast. It’s not locale-aware and that is totally okay. The real problem is an API (RRDs) which gives you a locale-tainted numeric. This was a problem in RRDs XML-export some time ago, maybe it’s still present here?

  2. rrdtool is doing the right thing, for a locale that has comma as a decimal point output the value as a decimal point. To me that makes a lot of sense.

    About the string to float conversion, its a little more than a type-cast but let’s say that’s all it is. The relevant page on string conversions says it uses strtod and friends and that underlying function IS locale aware.

    Anyway, PHP has a function floatval() that should work; except it gives the same wrong result as the type-cast.

Comments are closed.