So I have done a fair bit of research on how to store "money" in a database and I think the system I want to use is
Converting Money into CENTS and then storing the CENTS in a MySQL DB with a field type of DECIMAL (19,4).
My question is, IF I have an input field from the user... how do I deal with multiple input types.
IE:
$input = "1,346.54"
$input = "10,985.23"
$input = "110,400.50"
$input = "1033.44"
etc etc...
What would be the best method for converting this to CENTS? As we have to deal with 'strings' and convert them to INT, and divide by 100... Everything that I try throws issues because of the "comma" separation with the numbers.
Any thoughts or direction would be greatly appreciated.
function getMoneyAsCents($value)
{
// strip out commas
$value = preg_replace("/\,/i","",$value);
// strip out all but numbers, dash, and dot
$value = preg_replace("/([^0-9\.\-])/i","",$value);
// make sure we are dealing with a proper number now, no +.4393 or 3...304 or 76.5895,94
if (!is_numeric($value))
{
return 0.00;
}
// convert to a float explicitly
$value = (float)$value;
return round($value,2)*100;
}
Looks like there is a NumberFormatter class which provides a parseCurrency method. Have a look at http://www.php.net/manual/en/numberformatter.parsecurrency.php
The example provided is
$fmt = new NumberFormatter( 'de_DE', NumberFormatter::CURRENCY );
$num = "1.234.567,89 $";
echo "We have ".$fmt->parseCurrency($num, $curr)." in $curr\n";
You can remove the commas like this:
$input = str_replace( ',', '', $input);
At this point, you can convert to cents by converting to a float and multiplying by 100. However, this is probably unnecessary. You would potential encounter precision issues when performing math operations, but simply storing the values in the database can be done in the original form without alteration of the value (assuming your DB tables are properly structured):
$input = (float)str_replace( ',', '', $input);
function convert_to_cents($v)
{
$v = str_replace(',','',$v);
$p = explode('.',$v);
if(strlen($p[1])<2){ $p[1] = $p[1]*10;}
return ($p[0]*100)+$p[1];
}
This converts most of the decimal currencies to their subunits.
$1,234,567.89 = 123456789
£ 1,234,567.89 = 123456789
€1.234.567,89 = 123456789
12,34 EUR = 1234
12,34 € = 1234
12,30 € = 1230
1,2 = 102
function convertDecimalCurrencyToSubUnit($str)
{
if( preg_match('/^(.+)[^\d](\d|\d\d)[^\d]*$/', $str, $m) )
return intval(preg_replace('/[^\d]/', '', $m[1]) . ( (strlen($m[2])>1) ? $m[2] : ('0' . $m[2]) ));
return 0;
}
Probably just remove the ',' and the '.' from the string, the result is the amount in cents.
You will probably need to parse the string from the back using strrpos ... If you find a comma 2 spots from the end, then its prob safe to assume its foreign currency and those are the CENTS... Once you determine that, use a regex to strip the remaining commas (after you convert the "CENTS" comma to a decimal of course) ... Now you have a normal DEC number to play with.
Use this to find the last comma in your string ... strrpos
Use this to replace the commas preg_replace
Here is a helpful regex website .. regexlib
//Finding the last comma
$yourCommaPos = strrpos($myString, ',');
if ($yourCommaPos == strlen($myString) - 2) {
//Comma is foreign currency decimal
// Replace with '.'
} else {
//Not foreign Currency so...
//Strip Commas
preg_replace(',', '', $myString);
}
Related
How do I turn a thousand-comma separated string representation of an integer into an integer value in PHP? (is there a general way to do it for other separators too?)
e.g. 1,000 -> 1000
Edit (Thanks #ghost) Ideally, decimals should be handled, but I could accept a solution that truncates at a decimal point.
If thats simple as it gets you could use filter_var():
$number = '1,000';
$number = (int) filter_var($number, FILTER_SANITIZE_NUMBER_INT);
var_dump($number);
Or
$number = '1,000.5669';
$number = (float) str_replace(',', '', $number);
var_dump($number);
You can strip a specific character using str_replace, and cast as an integer using intval. A regular expression filter can also be used to determine if the input string is formatted correctly. Here is what that code might look like:
<?php
function remove_delimiters_simple($string, $delimiter = ',') {
// Removes all instances of the specified delimiter and cast as an integer
// Comma (,) is the default delimiter
return (int) str_replace($delimiter, '', $string);
}
function remove_delimiters_advanced($string, $delimiter = ',') {
// Use preg_quote in case our delimiter is '/' for some reason
// The regular expression should match validly formatted numbers using a delimiter
// every 3 characters
$valid_format_expression = sprintf(
'/^\d{1,3}(%s\d{3})*$/',
preg_quote($delimiter, '/')
);
// If not a validly formatted number, return null
if (! preg_match($valid_format_expression, $string)) {
return null;
}
// Otherwise, return the simple value
return remove_delimiters_simple($string, $delimiter);
}
If using PHP >= 5.3, you could use numfmt_create(), like:
$fmt = numfmt_create( 'nl_NL', NumberFormatter::TYPE_INT32 );
$num = "1,000";
echo numfmt_parse($fmt, $num); //gives 1000
Note::nl_NL is the locale you used in formatting number, and it should be the same when using for numfmt_create
$value = preg_replace("/[^0-9]+/", '', $value);
How could I edit this regex to get rid of everything after the decimal point? There may or may not be a decimal point.
Currently "100.1" becomes 1001 but it should be 100.
Complete function:
function intfix($value)
{
$value = preg_replace("/[^0-9]+/", '', $value);
$value = trim($value);
return $value + 0;
}
It is used to format user input for numbers as well as servers output to format numbers for the DB. The functions deals with very large numbers, so I can't use intval or similar. Any extra comments to improve this function are welcome.
You could just change the regex to /[^0-9].*/s.
.* matches zero or more characters, so the first character that is not a digit, and the digits that immediately follow, would be deleted.
You need to have a pattern that starts the search with a decimal place. At the moment you're only deleting the . not the numbers after it... So you could do '/\.[\d]+/'
$text = "1201.21 12 .12 12.21";
$text = preg_replace('/\.[\d]+/', '' ,$text);
The above code would result in $text = "1201 12 12"
Why not $value = round($value, 0);? This can handle large values and is meant to get rid of the following decimals mathematically (I'd rather work with numbers as numbers not as strings). You can pass PHP_ROUND_HALF_DOWN as a third parameter if you want to just get rid of the decimals 10.7 -> 10. Or floor($value); could work too.
how to convert all numbers to xx,xx decimal format without exploding number or string?
like;
8,9 --> 8,90
8,99 --> 8,99
12,1 --> 12,10
129,9 --> 129,90
any idea?
You can use number_format like:
$n = 2.1;
echo number_format($n, 2, ','); // 2,10
If you have commas as decimal separators in your input you can convert values to float with:
$number = floatval(str_replace(',', '.', str_replace('.', '', $string_number)));
str_replace('.', '', $string_number) is used to remove thousand separators.
str_replace(',', '.', ... ) is used to replace commas with dots.
If you have the intl extension enabled, the best approach to the issue (IMO) is given in the accepted answer to this question: PHP: unformat money
You can use
NumberFormatter::parseCurrency - Parse a currency number
Example from Manual:
$formatter = new NumberFormatter('de_DE', NumberFormatter::CURRENCY);
var_dump($formatter->parseCurrency("75,25 €", $curr));
gives: float(75.25)
Note that the intl extension is not enabled by default. Please
refer to the Installation Instructions.
After that, you'll have a float value and formatting that is a trivial issue using number_format().
If you do not have that extension, check out the regex approach in this question: What a quick way to clean up a monetary string
$money = number_format($number, 2, ",", ","); // $number must be a float, i.e 8.8
If your inputs have to have commas as decimal separators, do this too:
$money = "20,2";
$fixed_money = floatval(str_replace(",", ".", $money));
echo number_format($fixed_money, 2, ",", ",");
str_pad will add the missing zero
$val = '1008,95';
$len = strpos($val, ',') + 3;
$newval = str_pad($val, $len, '0', STR_PAD_RIGHT);
echo "Value: " . $newval . "<br>";
You have to be sure that your values don't have more than two decimal digits, though
I made a composer package that can deal with such things depending on which currency you are displaying.
https://packagist.org/packages/votemike/money
If you also enter the country it will format the currency in the local way (with or without commas and with the correct number of decimal places)
$money = new Money(99.50, 'JPY');
$money->format(); //¥100
And so if you just create a new money object with the amount you want to display and the currency you are displaying it in, calling ->format() on that object should give you the result you're looking for.
I'm doing a little application of adding prices and decimals. Points are normal to use with decimals, but how can I write decimal number with comma as input (543,35 instead of 543.35) and then maybe change it with point to the database (mysql)? Then print it back form the database with the comma. Reason is that comma (,) is more used in Finland than point (.) when write decimal numbers.
Thank you very much!
Samuel
$input = '5,50';
$output = str_replace(',', '.', $input);
var_dump($output); // string(4) "5.50"
$number = (float)$output;
var_dump($number); // float(5.5)
you need not do anything in the sql end. you want to format the decimal value in php (this assumes php4/php5): Set the third parameter $dec_point to ','
// string number_format ( float $number , int $decimals , string $dec_point , string $thousands_sep )
<?php
$number = 1234.56;
// French notation
$nombre_format_francais = number_format($number, 2, ',', ' ');
// 1 234,56
$number = 1234.5678;
// english notation without thousands seperator
$english_format_number = number_format($number, 2, '.', '');
// 1234.57
?>
source:
http://php.net/manual/en/function.number-format.php
Cheers!
the PHP number_format function is what you need :
number_format(5.50, 2, ',');
...should do the trick.
As it's been said, it's best to save the value as a float and then format for display. As an alternative to the previously suggested number_format(), you could try money_format() http://us.php.net/manual/en/function.money-format.php It doesn't work in a windows environment though if that is important to you.
No, using comma as a decimal separator for arithmetic operations is not supported. Filter your input and replace the comma with a period, then format your output as you wish.
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 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.
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.
http://p2p.wrox.com/topic.asp?TOPIC_ID=3099
goes through it step by step
[edit] typical...the site seems to be down now... :(
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)
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.
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
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?"