|   | php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login | 
| 
  [2009-02-17 09:45 UTC] cu19 at gmx dot de
 Description:
------------
In my script im using number_format to format values read from a MSSQL-DB via ODBC. Works fine, as long as the value is not equal to 1.9, 2.9, 3.9, 1.7 and probably some others.
I've made the following test script:
<?php
echo number_format(3.9, 2);
?>
Output is expected to be 3.90, but in fact is 9.8:
I don't know where this colon does come from, but for me it seems to be an error in number_format. Using sprintf('%4.2f', 3.9) leads to the same problem.
I'm using PHP 5.2.8 on Apache 2.2.9 on Windows XP MCE SP3. When Apache just got restarted, the test script works fine, but when executing my (quite large) script with several ODBC queries, it produces this error and after execution also the test script fails.
I've tried using 5.3 snapshot and 5.2.8 snap, the error occurs also. With PHP 5.2.6, the error didn't occur as far as I can remember. The error doesn't occur, too, with CLI.
Any idea? Or is it about the Apache webserver?
Reproduce code:
---------------
echo number_format(3.9, 2);
Expected result:
----------------
3.90
Actual result:
--------------
3.8:
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits             | |||||||||||||||||||||||||||||||||||||
|  Copyright © 2001-2025 The PHP Group All rights reserved. | Last updated: Fri Oct 31 07:00:01 2025 UTC | 
That's nothing different, but exactly the same problem as I described from the beginning. This is the function that I currently use as a workaround. It works fine, but I would prefer php doing this itself... function my_number_format ($number, $decimals=0, $dec_point=',', $thousands_sep='.') { $string = number_format($number, $decimals, $dec_point, $thousands_sep); $pos = strlen($string) - 1; $increase = false; while ($pos >= 0) { if (($increase) and (is_numeric($string{$pos}))) { $string{$pos} = chr(ord($string{$pos}) + 1); $increase = false; } if ($string{$pos} == ':') { $string{$pos} = '0'; $increase = true; } $pos--; } if ($increase) $string = '1' . $string; return $string; }/* set up an Excel 97-2003 spreadsheet with a worksheet 'Nov' and the row headers below on row 3 then add a row 14 with "John Smith" in Tenant, "11" in Lot, and "19" in Late Fee. Late fee should be number format with 2 decimals and comma thousands separator format. Create an odbc system DSN called "rent_2009" linked to it. Then run this script against it, I have stripped it down but I think it will show the problem. note this will NOT show the problem if you just supply variable values directly, it has to come from excel and has something to do with the (string) casting of values that excel thinks are numbers. somehow the float and string are getting goofed up. Even when I fixed the value, the total it was being added to still had the problem until I hardwired a second test on "18.:0" to fix that. Jeez. (row headers): Tenant Lot Prev Bal Rent LP Gas Taxes Garbage Elec CR Mowing Late Fee Misc Deposit New Bal Check Cash Mail Bal Fwd Last Mon This Mon Change Factor Gallons Rate/Gal LP Amt NOTES Misc Desc */ $result = ''; $month_total = 0; $totals = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); function FmtFieldAmt($tot_num, $field_name) { global $result, $month_total, $totals; $res = odbc_result($result, $field_name); $fld_amt = GetDec($res); $fmt_amt = FmtAmt($fld_amt); $month_total += $fmt_amt; $totals[$tot_num] += $fmt_amt; return $fmt_amt; } function GetDec($value) { if ($value == null || (string)$value == '') return 0; else return $value; } function FmtAmt($amt) { if ((string)$amt == '18.:') $amt = '19'; // another workaround for the total $cred = '    '; if ($amt < 0) { $amt = 0 - $amt; $cred = 'CR'; } // $amt = number_format($amt, 2) . $cred; // uncomment this comment out next line to see bug $ret = stupid_php($amt) . $cred; return $ret; } // this is my workaround which works if the value doesn't already have the stupid colon in it function stupid_php($val) { $len = strlen($val); if ($len == 0) return $val; if (strpos($val, '.') == false) return $val . '.00'; // for 123.4 len=5 pos=3 return 123.45 if (strpos($val, '.') == $len - 2) return $val . '0'; if (strpos($val, '.') < $len - 2) return round($val, 2); return $val; } function jsi_db_error($query, $errno, $error) { exit ('**Database Error (' .$errno. ') ' .$error. ' Query=[' .$query. '] **'); } $db_name = 'rent_2009'; $link = odbc_connect($db_name, 'user', 'pw') or jsi_db_error('Connect', odbc_errormsg(), odbc_error() ); $query = 'SELECT * FROM [Nov$A3:Z43] WHERE Lot = 11'; $result = odbc_exec($link, $query) or jsi_db_error($query, odbc_errormsg(), odbc_error()); echo FmtFieldAmt(6, 'Late Fee'); echo FmtAmt($totals[6]);I am not sure what you mean by "isolate the value which causes problems and pass it directly to number_format". If I just assign 19 to a variable (either as a string or as a number) and do this, it works fine. It is only when it comes from a result row value of a number-formatted excel spreadsheet cell that it shows the problem, for me. You could try a much simpler spreadsheet but I gave all the details of the one I am using that shows the problem, in case for some reason the spreadsheet details are related somehow (probably not though). I don't have time to install a new version of php right now, maybe later. I have already spent more time on this than I can afford. One other note, I see in playing around I changed some variable names in one function, it should really read: function FmtAmt($amt) { // another workaround for the total if ((string)$amt == '18.:') $amt = '19'; $cred = '    '; if ($amt < 0) { $amt = 0 - $amt; $cred = 'CR'; } // uncomment this and comment out next line to see bug // $ret = number_format($amt, 2) . $cred; $ret = stupid_php($amt) . $cred; return $ret; }Code to reproduce this, where lib.php contains the my_number_format function as described by cu19 at gmx dot de. Numbers are internally correct, but display incorrectly. <?php include_once("lib.php"); echo "== Demo 1 == <BR>"; $deb = 10.50; $crd = 1; $tot = 0; for ($t=1;$t<=300;$t++) { $tot += $deb - $crd; echo "$t $tot"; echo " >>>> "; echo strval($tot); echo " >>>> "; echo sprintf('%F',$tot); echo " >>>> "; echo number_format($tot,2); echo " >>>> "; echo my_number_format($tot,2); echo " >>>> "; echo "<BR>"; } ?>