Solved

# Calculations with negative currency numbers

Posted on 2014-02-13
241 Views
What is the easiest way of doing this

Input:

``````Product 1           \$21.95
Product 1(refund)  -\$21.95
Product 2           \$12.34
``````
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;
...
}
``````
Just wondering if there is a better way of doing this.
0
Question by:Julian Hansen
• 5
• 3

LVL 22

Assisted Solution

Ivo Stoykov earned 50 total points
ID: 39858417
Hi
try this
``````\$amount = preg_replace("\D", "", \$input);
``````
HTH

Ivo Stoykov
0

LVL 51

Author Comment

ID: 39858471
@Ivo - I am assuming you meant
``````\$amount = preg_replace("/\D/", "", \$input);
``````
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

LVL 108

Expert Comment

ID: 39858594

``````<?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";
}
``````
0

LVL 51

Accepted Solution

Julian Hansen earned 0 total points
ID: 39858669
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)
``````

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]);
...
}
``````
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);
``````

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

LVL 108

Assisted Solution

Ray Paseur earned 450 total points
ID: 39858731
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

LVL 51

Author Comment

ID: 39858751
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

LVL 108

Expert Comment

ID: 39859462
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';
}
``````
0

LVL 51

Author Comment

ID: 39860135
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

LVL 51

Author Closing Comment

ID: 39860150
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

## Featured Post

### Suggested Solutions

Deprecated and Headed for the Dustbin By now, you have probably heard that some PHP features, while convenient, can also cause PHP security problems.  This article discusses one of those, called register_globals.  It is a thing you do not want.  …
These days socially coordinated efforts have turned into a critical requirement for enterprises.
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…