tags:

views:

617

answers:

7

I have a form in which people will be entering dollar values.

Possible inputs:
$999,999,999.99
999,999,999.99
999999999
99,999
$99,999

The user can enter a dollar value however they wish. I want to read the inputs as doubles so I can total them.

I tried just typecasting the strings to doubles but that didn't work. Total just equals 50 when it is output:

$string1 = "$50,000";
$string2 = "$50000";
$string3 = "50,000";
$total = (double)$string1 + (double)$string2 + (double)$string3;
echo $total;
A: 

http://p2p.wrox.com/topic.asp?TOPIC_ID=3099

goes through it step by step

[edit] typical...the site seems to be down now... :(

tim_yates
I was able to pull it up. Looking over it now.
Haabda
Well, the test code on the post works. However, since I'm pretty new to this RegEx stuff, I'm not quite sure how to extract just the numbers and add them together.
Haabda
I thought you just wanted to check it was valid? If you want the number, I guess you can use string functions to remove the $ and , chars (see mainland european locality issues in Frosty's answer as well), and then parse the result as a float?
tim_yates
Ahhh...you added the numeric requirement after I originally answered ;)
tim_yates
A: 

not a one liner, but if you strip out the ','s you can do: (this is pseudocode)

 m/^\$?(\d+)(?:\.(\d\d))?$/
 $value = $1 + $2/100;

That allows $9.99 but not $9. or $9.9 and fails to complain about missplaced thousands separators (bug or feature?)

There is a potential 'locality' issue here because you are assuming that thousands are done with ',' and cents as '.' but in europe it is opposite (e.g. 1.000,99)

Frosty
This will be a form used by a US company.
Haabda
England is in Europe and does it the same way as the US
tim_yates
+2  A: 

A regex won't convert your string into a number. I would suggest that you use a regex to validate the field (confirm that it fits one of your allowed formats), and then just loop over the string, discarding all non-digit and non-period characters. If you don't care about validation, you could skip the first step. The second step will still strip it down to digits and periods only.

By the way, you cannot safely use floats when calculating currency values. You will lose precision, and very possibly end up with totals that do not exactly match the inputs.

Update: Here are two functions you could use to verify your input and to convert it into a decimal-point representation.

function validateCurrency($string)
{
    return preg_match('/^\$?(\d{1,3})(,\d{3})*(.\d{2})?$/', $string) ||
           preg_match('/^\$?\d+(.\d{2})?$/', $string);
}

function makeCurrency($string)
{
    $newstring = "";

    $array = str_split($string);
    foreach($array as $char)
    {
        if (($char >= '0' && $char <= '9') || $char == '.')
        {
            $newstring .= $char;
        }
    }

    return $newstring;
}

The first function will match the bulk of currency formats you can expect "$99", "99,999.00", etc. It will not match ".00" or "99.", nor will it match most European-style numbers (99.999,00). Use this on your original string to verify that it is a valid currency string.

The second function will just strip out everything except digits and decimal points. Note that by itself it may still return invalid strings (e.g. "", "....", and "abc" come out as "", "....", and ""). Use this to eliminate extraneous commas once the string is validated, or possibly use this by itself if you want to skip validation.

Derek Park
What should I use instead of a float then?
Haabda
You should use fixed point (instead of floating point). A simplified explanation is that instead of dollars (which have fractions) you store the number of pennies -- assuming you'll never deal with fractions of pennies. If need be, you can store the number of hundredths of pennies. So $1.25 == 125.
davr
Haabda, Davr's suggestion to use fixed point will probably be sufficient for your needs. If you need really large currency amounts, I'd recommend that you look for a currency class designed specifically to handle large currency amounts (I'd bet someone has written one for PHP).
Derek Park
A: 

I recommend not to use a float for storing currency values. You can get rounding errors if the sum gets large. (Ok, if it gets very large.)

Better use an integer variable with a large enough range, and store the input in cents, not dollars.

Treb
A: 

I belive that you can accomplish this with printf, which is similar to the c function of the same name. its parameters can be somewhat esoteric though. you can also use php's number_format function

The Brawny Man
+2  A: 

You don't ever want to represent monetary values as floats!

For example, take the following (seemingly straight forward) code:

$x = 1.0;
for ($ii=0; $ii < 10; $ii++) {
  $x = $x - .1;
}
var_dump($x);

You might assume that it would produce the value zero, but that is not the case. Since $x is a floating point, it actually ends up being a tiny bit more than zero (1.38777878078E-16), which isn't a big deal in itself, but it means that comparing the value with another value isn't guaranteed to be correct. For example $x == 0 would produce false.

troelskn
No, thats just a reason never to use == with floating point values.
Shinhan
A: 

Assuming that you are getting real money values, you could simply strip characters that are not digits or the decimal point:

(pseudocode) 
newnumber = replace(oldnumber, /[^0-9.]/, //)

Now you can convert using something like

double(newnumber)

However, this will not take care of strings such as "5.6.3" and other such non-money strings. Which raises the question, "Do you need to handle badly formatted strings?"

Ben Doom