Tuesday, November 4, 2008

PHP and outputting floats

I was writing a script in PHP to compare two data sources, and in the report I was outputting using code like this:
echo "A={$vA}, B={$vB}\n";

What was strange is I was getting entries like
A=3.4E+6, B=340000
A=1.4E+6, B=1500000
A=2146666.66667, B=2.3E+6

My first guess was some data was int, so I added asserts for that, but, no, everything is float.

As often happens, the answer was to be found in the user contributed notes in the PHP online manual: http://jp.php.net/manual/en/language.types.float.php#83577

I just have to repeat his final sentence: I have to be honest: this is one of the strangest things I have seen in any language in over 20 years of coding, and it is a colossal pain to work around.

Also in the notes are complaints that output of floating point numbers uses the locale. Using number_format is one solution for that, or using %F in sprintf is also locale-independent apparently. So my code becomes:
echo sprintf("A=%F, B=%F\n",$vA,$vB);

Unfortunately that now gives me entries like:
A=481.000000,B=1150000.000000

I have hit this zero-noise issue using sprintf in C++ too.

So here is my solution (be aware that this adds CPU cycles):

/**
* Format a float, only showing significant decimal places
*
* @param float $v
* @return String
*/
function fmt_float($v){
$s=(string)$v;
if(strpos($s,'E+')===false)return $s;
$s=sprintf("%.9F",$v); //Write with all decimal places
$s=rtrim($s,'0'); //Chop off trailing zeroes
$s=rtrim($s,'.'); //Chop off decimal point if it is left at the end.
return $s;
}
...
echo "A=".fmt_float($vA).", B=".fmt_float($vB)."\n";

I'll throw that into the next fclib release. Can anyone make a better version?

Note: The first two lines mean use PHP's built-in float to string conversion by default and just use our own when it ended up in scientific notation. I found this gave better results (because using %.9F sometimes writes 120.0000000001 instead of simply 120.000000000, whereas the PHP built-in conversion is fine (giving "120") ). However if you also needed a locale-independent solution, then uncomment those first two lines (as PHP's built-in conversion uses locale).


Monday, October 20, 2008

Being emailled followups

A question about Blogspot seems best posted here! I noticed there had been some comments on other entries. Surely I'm not supposed to poll each article to see if there has been a new comment. I'm sure there was a setting to say to email me all follow-ups to all articles, but a recent search turned up nothing.
Anyway, thanks for the replies!! I want to try out the Actionscript suggestions I received and will then update the articles if they work (or even if they don't).

Wednesday, October 15, 2008

Microsoft SQL server and PHP

I'm a big fan of Pear::DB. (And, as an aside, I think the Pear decision to deprecate it was a bit strange.) But it didn't work for me for Microsoft SQL server (aka mssql), for one particular project (I'm fairly sure it has worked on other projects, so I think it was something specific to this environment). What did work was using the COM object.

COM objects from PHP are a bit of a black art, perhaps as most code examples seem to be in Visual Basic, which has a very different syntax. I found the following page useful, as it shows all three ways of running the same query for a mssql database:
http://www.webcheatsheet.com/PHP/connect_mssql_database.php

I think I also used the user comments in the online PHP manual.

One thing the above URL does not show is error reporting. Here is the code I developed, which also takes care of not treating 5701 and 5703 as errors (congrats to Microsoft on another poorly thought out API!):

//$db is the return from the new COM() call.
//...run some Open or Execute command on $db...
$errors=$db->Errors;
if($errors->Count==0)return ""; //It worked
$e='';
foreach($errors as $ix=>$error){
if($error->NativeError==5701 || $error->NativeError==5703)continue; //Information messages, not errors
$e.="Number={$error->Number}, Source={$error->Source}, SQLState={$error->SQLState}, NativeError={$error->NativeError}, Description={$error->Description}\n";
}
return $e;

Sunday, July 20, 2008

Centering multiline text fields in AS3?

This seems to be a regression-without-cure between AS2 and AS3. In AS2 I could write:
txt.wordWrap=true;
txt.align="center";
txt.htmlText="blah, blah, blah";

and each line in that text field would be centered.

In AS3 I am writing:
txt.wordWrap=true;
txt.align=TextFieldAutoSize.CENTER;
txt.htmlText="blah, blah, blah";

but it ends up left-aligned.
The bigger problem is that this is the documented behaviour: if wordWrap is set then align will not center text.

So, how do I center a multi-line text field?!!

(Time passes.)

I cannot believe there is nothing on the web about this. Have all the flash developers in the world stopped centering their text, or am I missing something obvious?

(More time passes.)

OK, after some trial-and-error I have a workaround, which sucks but will get the job done:

CopyA.htmlText='<body><p align="center">blah, blah, blah</p></body>';

Why do I say this sucks? Because I cannot (easily) apply it afterwards: I will need to get the current htmlText contents, detect the body tag and insert after it; and a corresponding closing-P tag at the end.

But in this application the text string comes from a DB and is allowed to contain HTML tags. What if it too already has a P tag? Will I get an extra blank line? Are P tags allowed to be nested?
(Well, I just tried, and it seems fine still, and if the inner P tag has an align that is the one that gets applied. So all good there.)

UPDATE: I discovered I can also write this:
CopyA.htmlText='<p align="center"><body>blah, blah, blah</body></p>';
That is dubious, and I wonder if it is future-proof, or if I am exploiting a bug? But it is obviously far easier, as it saves me having to do any parsing - I can just wrap whatever the existing htmlText is:
copyA.htmlText='<p align="center">'+copyA.htmlText+'</p>';