Calculations with negative currency numbers

What is the easiest way of doing this

Input:

Product 1           $21.95
Product 1(refund)  -$21.95
Product 2           $12.34

Open in new window

I need to read the values from each line. I don't need to preserve the currency symbol but I do need to do calculations with the value.
Currently using
if (preg_match('/(.*)\s+(-?)\$(\d+.\d\d)/', $input, $r)) {

    //And then
    $amount = $r[3];
    if ($r[2] == '-') $amount *= -1;
...
}

Open in new window

Just wondering if there is a better way of doing this.
LVL 65
Julian HansenAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Ivo StoykovCommented:
Hi
try this
$amount = preg_replace("\D", "", $input);

Open in new window

HTH

Ivo Stoykov
0
Julian HansenAuthor Commented:
@Ivo - I am assuming you meant
$amount = preg_replace("/\D/", "", $input);

Open in new window

The code you posted gives an error
preg_replace(): Delimiter must not be alphanumeric or backslash

The \D will leave only digits and I loose the minus sign and the full stop.

The preg_replace does not help me though as I need to extract the currency as part of a wider pattern match - I can't preprocess the string with a preg_replace.

I have many different lines in the file with different patterns I am looking for. Each pattern is unique and will only match one type of line. Therefore the currency with symbol and minus needs to be extracted as part of a single preg_match that also matches other pattrns.

I could apply a preg_replace on the extracted currency value but I can't see how that is better than the solution I am currently using.
0
Ray PaseurCommented:
Please see http://www.laprbass.com/RAY_temp_julianh.php

<?php // RAY_temp_julianh.php
error_reporting(E_ALL);

// SEE http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28364779.html

// TEST DATA FROM THE POST AT EE
$inp = <<<EOD
Product 1           $21.95
Product 1(refund)  -$21.95
Product 2           $12.34
EOD;

// USE A REGULAR EXPRESSION THAT KEEPS ONLY THE NUMBER-VALUE DATA
$rgx
= '#'         // REGEX DELIMITER
. '['         // START CHARACTER CLASS
. '^'         // NEGATION - MATCH NONE OF THIS CLASS
. '0-9'       // NUMBERS
. '.-'        // DECIMAL POINT AND MINUS SIGN
. ']'         // END CHARACTER CLASS
. '#'         // REGEX DELIMITER
;

// MAKE AN ARRAY SO SE CAN PROCESS EACH LINE ITERATIVELY
$arr = explode(PHP_EOL, $inp);

// PROCESS EACH LINE
foreach ($arr as $str)
{
    // LOCATE THE NUMBER AT THE END
    $sub = explode(' ', $str);
    $end = end($sub);
    $end = preg_replace($rgx, NULL, $end);

    // SHOW THE WORK PRODUCT
    echo PHP_EOL . "<br>$str => $end";
}

Open in new window

0
Cloud Class® Course: Amazon Web Services - Basic

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

Julian HansenAuthor Commented:
Thanks Ray - but I think the solution I have already is more optimal.

Exploding the strings on a space won't really help as I would then have to reconstruct the product description string - which might contain N words. In addition I have other patterns I am matching on that contain currency values that do not lend themselves to being split on word boundaries.

Essentially your solution is using the preg_replace option on the extracted currency value which could have been obtained from my original preg_match like so

preg_match('/(.*)\s+(-\$\d+.\d\d)/', $input, $r)

Open in new window


And then applying the preg_replace to $r[2] giving
if (preg_match('/(.*)\s+(-?\$\d+.\d\d)/', $input, $r)) {

    //And then
    $amount = preg_replace("/[^0-9\.-]/", "", $r[2]);
...
}

Open in new window

I am not convinced that the use of preg_replace is better than checking for the minus and modifying the $amount value accordingly.

I was really looking for a "better" way of dealing with this than the one I am currently using.

Another option is as follows
$amount = trim($r[2], '-$') * (($r[2][0] == '-') ? -1 : 1);

Open in new window


All work - just looks messy.
Didn't want to go this route and then find out there was some function or trick that could get the same result more elegantly.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Ray PaseurCommented:
As usual the quality of the solution is going to be directly related to the quality of the test data.  If your test data really consists of a string variable with multiple lines delimited by EOL characters and containing freeform data, including an arbitrary number of blanks to pad the lines, with the money-format strings at the end of each line, then you've got a tested and working solution.  You can try to "optimize" this, but I expect that will be like milking a mouse - no matter how much effort you put into the process, you won't get much of a result.

For dollars, the characters you want to keep are the digits, and the optional minus sign and the optional decimal point.  If you're dealing with currencies in some European countries, you need to consider the different meanings of the decimal point and the comma.  The central objective is to reduce the string representations of the currency values to a numeric representation that PHP can use to do arithmetic, that SQL can store in a data base, etc.  I have some experience with PHP and arithmetic and I know of no function that can reverse the effects of number_format() or money_format() because these functions turn float values into string representations.  The are intended to prepare internal data for document output.  In the instant case, it looks like you're working with document output, but using it as input to an additional process.

PHP has rules governing the way string representations of numbers can be converted to numbers.  If all you had to deal with was the dollar sign, you might just use str_replace() to remove the dollar sign.  Then you could let PHP duck-typing work on your behalf and you could do arithmetic with the resulting string representations of number values.
http://www.php.net/manual/en/language.types.string.php#language.types.string.conversion

The way PHP handles loose data types has not always been consistent.
http://www.php.net/manual/en/migration51.integer-parameters.php
http://www.php.net/manual/en/language.types.type-juggling.php

If you have time to spend on the project, you might experiment with sscanf(), but I doubt if I would bother with that.  I've used the regular expression I posted above in many applications and it's always done the job for me.  The rest of the script is just about isolating the dollar values which are intermixed into the test data string, and there are many ways of doing that.
0
Julian HansenAuthor Commented:
Thanks Ray, the question was about the best way of turning -$21.95 into a number - the parsing of the string is not in question.

My main objective was to discover if there was a more elegant approach than the one I was using - I think the follwoing comment sums it up
I know of no function that can reverse the effects of number_format() or money_format()

I will use either my original solution or the solution in my last post.
0
Ray PaseurCommented:
Well, that changes the question a bit.  Use whatever you feel comfortable with -- I feel comfortable with this regular expression.

<?php // RAY_temp_julianh.php
error_reporting(E_ALL);

// SEE http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28364779.html#a39858751

// TEST DATA FROM THE CLARIFIED EXPLANATION AT EE
$inp = '-$21.95';

// USE A REGULAR EXPRESSION THAT KEEPS ONLY THE NUMBER-VALUE DATA
$rgx
= '#'         // REGEX DELIMITER
. '['         // START CHARACTER CLASS
. '^'         // NEGATION - MATCH NONE OF THIS CLASS
. '0-9'       // NUMBERS
. '.-'        // DECIMAL POINT AND MINUS SIGN
. ']'         // END CHARACTER CLASS
. '#'         // REGEX DELIMITER
;

// IS IT NUMERIC?
echo "<br>$inp";
if (is_numeric($inp))
{
    echo ' IS NUMERIC';
}
else
{
    echo ' IS NOT NUMERIC';
}

// REDACT THE STRING VARIABLE
$new = preg_replace($rgx, NULL, $inp);

// IS IT NUMERIC?
echo "<br>$new";
if (is_numeric($new))
{
    echo ' IS NUMERIC';
}
else
{
    echo ' IS NOT NUMERIC';
}

Open in new window

0
Julian HansenAuthor Commented:
Thanks Ray, apologies if the question was phrased in a confusing way.

I do not need code for parsing the line - I feel my approach wth preg_match is optimal for for the requirements of the project.

I was only trying to find out if there was a standard way with converting currency strings back into floating point values so they can be used in calculations.

Given there does not seem to be a standard way of doing this I will leave my solution as it stands.
0
Julian HansenAuthor Commented:
I am choosing my own solution as none of the answers posted resulted in my changing my implementation.

I have awarded a B grade as the solutions presented were not used but they could provide some info to others reading this post.

@Ivo - points for the \D - did not ultimately do what I needed but it did lead to my solving another related problem.

@ray - comprehensive as always - and useful to whoever might read this link.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
PHP

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.