How to round amounts correct? - php

I have a little problem with round() in php.
I don't know, if I really make it correct.
(it is an order system)
$amount can be decimal 14,8
$price can be decimal 10,5 (in the database)
I am using the following rounding at this moment
The $price is for one $amount
function round_amount($amount) {
return (float) round($amount, 8);
}
function round_price($amount) {
return (float) round($amount, 5);
}
//if some one have more decimal chars like 10.000000009
round_amount(10.000000009); //will be 10.00000001
//if some one have more decimal chars like 10.000009
round_price(10.000009); //will be 10.00001
//also this is possible
round_price(round_amount(10.000000009)*round_price(10.000009))
Is this way correct to use round?
Some user are using more than 16 decimals.
I am deducting / adding the results in the user database.
But I see, that some user have about 5-10 cents too much!
Is the only way to resolve this, to allow ONLY 8 and 5 decimals?
And warn the user, if he tries to use more?
But than I will get an problem with the round_cur(round_amount(10.000000009)*round_cur(10.000009))
I hope some one understand what I am meaning, or can tell me, if my way to round is correct.
$amount = 10.12398413498579889173459873;
$price = 5.1938457198347695;
echo round_cur(round_amount($amount)*round_cur($price))."<br />";
echo round_cur($amount*$price);
//returns
//52.58245
//52.58241
Interesting!

Why not keep the actual value from the database cached and then have a separate variable for display. In that way all of the calculations can be done on the cached, correct, value while maintaining a clean UI. Just make sure to update the displayed variable each time the cached variable is updated.
Also, always apply math before doing any rounding. Multiplying two exact numbers is much more accurate than multiplying two rounded numbers. This applies for every operation in mathematics. So add, subtract, divide, multiply and then round, but never use that rounded number for another formula. Use the previous exact number.

I think you way is correct, but depend on your situation as you mentioned, those two formulas return different values and you are the one as the manager of the project which system is better for you (you should think about what is best for you and what is best for user and make up your mind and see if you choose either, what would be the trade of) and for this situation i recommend the first methid
round_price(round_amount(x)*round_price(y))
And for your problem a good notice or warning should do it. Give an example for user.

Related

php fmod returning unusable result

fmod(floatval("314.6"), floatval("1.3"))
=> 1.1990408665952E-14
I understand more or less the underlying problem of representing the numbers in binary form and something with IEEE-754. But: How would I get a result that is usable in pratice?
Example what I want to achieve: A merchant can define the price P of his product, but the price has to be a multiple of x:
if (fmod(P, x) != 0) { echo "price must be a multiple of " . x; }
It would be so simple, but the approach fails whenever I get something like 1234E-18 as return value of fmod().
What to do in real life to check the price interval easily without using custom code?
This or similar questions have been around, but all I can find are explanations why fmod() behaves like it does. Never an answer how to solve this real-life problem...
The problem here is that 314.6 is exactly 1.3 * 242 so floating point remainder is zero but you get 0.00000000000001199041 due to the IEEE 754 inaccuracies you're well aware of.
But don't forget one of the rules of floating point maths: you can't compare floats for equality carelessly. If your arguments have one decimal position you don't need 14-position accuracy in your results. You have prices: how many decimals make sense in your currency? If you were using e.g. euros you're unlikely to use more than two (cents) and 1.1990408665952E-14 is zero to all effects:
var_dump(round(1.1990408665952E-14, 2));
double(0)
Some people recommend doing all price maths using integers (e.g. cents instead of euros) to avoid rounding errors but most real life issues come from very specific errors:
Displaying raw floats to user (rather than rounding and formatting them).
Doing raw comparisons (if ($foo == $bar)).
… and integers don't prevent all rounding errors anyway (e.g. tax calculations on individual items not matching calculations on invoice totals).

random function: higher values appear less often than lower

I have a tricky question that I've looked into a couple of times without figuring it out.
Some backstory: I am making a textbased RPG-game where players fight against animals/monsters etc. It works like any other game where you hit a number of hitpoints on each other every round.
The problem: I am using the random-function in php to generate the final value of the hit, depending on levels, armor and such. But I'd like the higher values (like the max hit) to appear less often than the lower values.
This is an example-graph:
How can I reproduce something like this using PHP and the rand-function? When typing rand(1,100) every number has an equal chance of being picked.
My idea is this: Make a 2nd degree (or quadratic function) and use the random number (x) to do the calculation.
Would this work like I want?
The question is a bit tricky, please let me know if you'd like more information and details.
Please, look at this beatiful article:
http://www.redblobgames.com/articles/probability/damage-rolls.html
There are interactive diagrams considering dice rolling and percentage of results.
This should be very usefull for you.
Pay attention to this kind of rolling random number:
roll1 = rollDice(2, 12);
roll2 = rollDice(2, 12);
damage = min(roll1, roll2);
This should give you what you look for.
OK, here's my idea :
Let's say you've got an array of elements (a,b,c,d) and you won't to randomly pick one of them. Doing a rand(1,4) to get the random element index, would mean that all elements have an equal chance to appear. (25%)
Now, let's say we take this array : (a,b,c,d,d).
Here we still have 4 elements, but not every one of them has equal chances to appear.
a,b,c : 20%
d : 40%
Or, let's take this array :
(1,2,3,...,97,97,97,98,98,98,99,99,99,100,100,100,100)
Hint : This way you won't only bias the random number generation algorithm, but you'll actually set the desired probability of apparition of each one (or of a range of numbers).
So, that's how I would go about that :
If you want numbers from 1 to 100 (with higher numbers appearing more frequently, get a random number from 1 to 1000 and associate it with a wider range. E.g.
rand = 800-1000 => rand/10 (80->100)
rand = 600-800 => rand/9 (66->88)
...
Or something like that. (You could use any math operation you imagine, modulo or whatever... and play with your algorithm). I hope you get my idea.
Good luck! :-)

Why are the decimals not adding up correctly?

I'm working on a program where you can choose up to 3 things you want to divvy points amongst.
Say for example that an action gains you 4 points, and those 4 points are divvied amongst the 3 things you selected.
In this case, those 3 things each get 1.33333... points.
In my database, they are stored as 1.33.
However when I bring them out, it tallies up to 3.99.
Understandable.
But how can I avoid this without giving one of the things 1.34 points?
Store the full float/double in your database rather than truncating to 2 decimal places. The time to trunc is when displaying the value to the user -- but only trunc the displayed string, not the actual value.
Floating point values are the annoying drunk uncle of computing. Just let them be what they are, and then clean them up when presenting to the public eye.
Floating point numbers will be lossy in this case. If you are dealing with integer numerators and denominators, why not store the numbers as fractions? You can make use of Pear's Math Fraction library or write something yourself.
Use a third decimal place - not for display, but only for tracking precision. If someone divides 4 points among three, store it as 1.333. When you calculate back, you get 3.999 which you round up to 4. On the other hand, if someone divides 3.99 among three objects, store it as 1.33, so when you calculate back, you get 3.99 (and not 3.999) and thus you know not to round up.

PHP Large Float, need to round

I have a program that is giving me this number: 9.1466606511048E-8
I need to round that, using most obvious functions in PHP gives me 0.
That's because that number has a value of 0.000000091466606511048, which is very close to zero. Perhaps you need to be more specific about what sort of rounding you want.
How many decimals are you attempting to round to?
The number that you specified is the same as: 0.000000091466606511048
If you're using the round function without specifying to what decimal place you want to round, then it is going to round to zero because of the value.

Add all lines multiplied by another line in another table

I hope I can explain this good enough. I have 3 tables. wo_parts, workorders and part2vendor. I am trying to get the cost price of all parts sold in a month. I have this script.
$scoreCostQuery = "SELECT SUM(part2vendor.cost*wo_parts.qty) as total_score
FROM part2vendor
INNER JOIN wo_parts
ON (wo_parts.pn=part2vendor.pn)
WHERE workorder=$workorder";
What I am trying to do is each part is in wo_parts (under partnumber [pn]). The cost of that item is in part2vendor (under part number[pn]). I need each part price in part2vendor to be multiplied by the quantity sold in wo_parts. The way all 3 tie up is workorders.ident=wo_parts.workorder and part2vendor.pn=wo_parts.pn. I hope someone can assist. The above script does not give me the same total as when added by calculator.
This is not an answer, just a comment.
Why don't you take the sum/multiply operation outside the SQL statement? I know, that seems stupid because it will increase the lines of code and the complexity of the script, but, imho, it is always a good thing to keep code and SQL statements as far away as possible.
The key cause I could see for something like this would be a type issue. For example, this could happen if you are using FLOATs instead of NUMERICs, you might get a slightly different answer. That is a mistake that is way too common, btw.
I would recommend double checking your schema to make sure you are using NUMERICs across the board here. NUMERIC is crazy-powerful on PostgreSQL, it performs very well, and it properly supports arbitrary precision operations. If you can't change the data type, cast your fields to numeric in your query.
FLOAT types (including DOUBLE) are fixed precision binary numbers, and they don't correspond exactly to base 10 numbers always. NUMERICs are stored internally as base 1000 (meaning 9 digits per 30 bits), and this is very efficient to convert to/from binary. The precision is also arbitrary, although it does have a maximum. However, for financial stuff, the maximum values or precision are not an issue with numeric data types. You should use them liberally.

Categories