i'm hoping someone can help with my question.
Let's say I have a quantity which is simply an integer in a variable. I know that if the quantity is below 10, the cost will be 1.50. Between 10-20 the cost would be 1.50 x 2, then between 20-30 the cost would be 1.50 x 3 and so on.
The bit im stuck on is the "and so on". I know how I could code this if I knew there was a limit of let's say 100, however if it can be limitless, how could I code this in an intelligent way to work it out for me?
Sorry for the example-less question. I wouldn't know where to start with this query.
Thank you for your time
Since your steps are in tenfold, you can simply divide your number by 10 to get the amount of 1.5's you'll need to calculate the cost.
Then, we'll use ceil() to round the divison to the next integer. Now, for example, 5/10 = 0,5 results in return 1.5 * 1;
Take a look at this example;
<?php
function getCost($n) {
return 1.5 * ceil($n / 10);
}
var_dump(getCost(5)); float(1.5)
var_dump(getCost(15)); float(3)
var_dump(getCost(25)); float(4.5)
var_dump(getCost(35)); float(6)
var_dump(getCost(100)); float(15)
var_dump(getCost(12345)); float(1852.5)
Try it online!
I am using the moneyphp/money class to store monetary values. However when calculating the tax owed I have an issue where the calculated tax is a decimal and the library is looking for an integerish value.
Example:
$invoiceTotal = new Money("155" new Currency("USD")); //$1.55
$taxRate= 0.065;
$invoiceTotalWithTax = $invoiceTotal->multiply($taxRate);
echo $invoiceTotalWithTax; //0.10 whereas actual value is 1.55*0.065 = 0.10075
$formatter = new DecimalMoneyFormatter();
$formatter->format($invoiceTotalWithTax); //will return $0.10
From the above example, some fractional cent value is being lost. Individually it's not a lot, however if we process several thousand invoice in a tax period, the total tax collected will eventually surpass 1 cent.
Is there a way to handle these situations with the Money package?
If not, then is there another package that can handle this?
Shameless plug: I don't know if there's a way to do it with the moneyphp/money library, but here's how you can handle this situation with the brick/money library (disclaimer: I authored it).
The option you choose will depend on what you're trying to achieve.
Option 1: use a Money with the default scale, round up or down
Use this method if you need the result in the default scale for the currency (2 decimal places for USD), and know which rounding to apply:
use Brick\Money\Money;
use Brick\Math\RoundingMode;
$invoiceTotal = Money::ofMinor('155', 'USD'); // USD 1.55
// or
$invoiceTotal = Money::of('1.55', 'USD');
$taxRate = '0.065'; // prefer strings over floats!
$totalWithTax = $invoiceTotal->multipliedBy($taxRate, RoundingMode::DOWN); // USD 0.10
$totalWithTax = $invoiceTotal->multipliedBy($taxRate, RoundingMode::UP); // USD 0.11
You have many more rounding modes to choose from. If you don't provide a rounding mode, and the result does not fit into 2 decimal places, you'll get an exception.
Option 2: use a Money with a custom scale
If you need to work with a given precision, say 5 decimal places, you can specify this when you create the Money:
use Brick\Money\Money;
use Brick\Money\Context\CustomContext;
use Brick\Math\RoundingMode;
$invoiceTotal = Money::of('1.55', 'USD', new CustomContext(5)); // USD 1.55000
$taxRate = '0.065';
$totalWithTax = $invoiceTotal->multipliedBy($taxRate); // USD 0.10075
If the result does not fit into 5 decimal places, you'll need to provide a RoundingMode, or you'll get an exception.
Option 3: use a Money with auto scale
Use this method to automatically adjust the scale of the result to the correct number of decimal places:
use Brick\Money\Money;
use Brick\Money\Context\AutoContext;
use Brick\Math\RoundingMode;
$invoiceTotal = Money::of('1.55', 'USD', new AutoContext()); // USD 1.55
$taxRate = '0.065';
$totalWithTax = $invoiceTotal->multipliedBy($taxRate); // USD 0.10075
No rounding mode is involved, but if a division yields a decimal number with an infinite number of digits, you'll get an exception.
Option 4: use a RationalMoney
A RationalMoney is a money object that represents its amount as a rational number (a fraction). It's particularly useful when you need to chain several operations with no rounding whatsoever:
use Brick\Money\Money;
use Brick\Math\RoundingMode;
$amount = Money::of('1.55', 'USD'); // USD 1.55
$amount = $amount->toRational(); // USD 155/100
$amount = $amount->dividedBy(3); // USD 155/300
$amount = $amount->dividedBy(7); // USD 155/2100
Once you have performed all your operations, you can convert your final number to a decimal Money, using a rounding mode if necessary:
use Brick\Money\Context\DefaultContext;
use Brick\Money\Context\CustomContext;
$amount->to(new DefaultContext(), RoundingMode::DOWN); // USD 0.07
$amount->to(new CustomContext(6), RoundingMode::DOWN); // USD 0.073809
Final considerations
The brick/money package offers formatting, cash roundings, money allocation, currency conversion, and more. It is based on the brick/math package, that performs calculations on numbers of any scale. Give it a try!
I am trying to solve this problem:
When I purchase items I receive a receipt which lists the name of all
the items and their price (including tax), finishing with the total
cost of the items, and the total amounts of sales taxes paid. The
rounding rules for sales tax are that for a tax rate of n%, a shelf
price of p contains (np/100 rounded up to the nearest 0.05) amount of
sales tax.
So, ... I have a price in php:
$value = 11.25;
and I don't understand why
var_export(ceil($value * 0.05 * 10));
returns 6 and dividing per 10, the result is
var_export(ceil($value * 0.05 * 10) / 10);
0.59999999999999998
some nice experiments:
php > echo bcmul(11.25, 0.05, 3);
0.562
php > echo bcmul(ceil(11.25), 0.05, 3);
0.60
You could read what bishop point out and also you could read this in order to understand what is the problem.
Now talking about a solution, you could use PHP BCMath extension which should be used when you want to work with precision mathematical numbers
For arbitrary precision mathematics PHP offers the Binary Calculator
which supports numbers of any size and precision, represented as
strings.
One solution (a ugly one) could be this
$value = 11.25;
var_export(bcdiv(ceil($value * 0.05 * 10), 10, 1)); // Output '0.6'
Here I am using the bcdiv from the mentioned extension.
I'm modifying one of OpenCart's product filters to filter products in/out by price. What I do is get all products belonging to a certain category and extract their prices to put them in a slider, but this is not elegant or 'professional' at all, and I would like to code a proper solution.
Let's say I have the following prices: 125, 270, 517, 1680 and 14790. What I would like to do (ideally) is get the highest number (14790 in this short example) and, from it, get something like '15000', so I can divide that between a given factor (like 100) and put that into a slider.
Is there a PHP function to do this kind of calculation?
If I understand your question, and you're asking to round to essentially the nearest 100, there isn't a specific function, but with a bit of maths you can round to the nearest hundred like so:
$price = ceil($price / 100) * 100;
Using:
$price = ceil($price / 1000) * 1000;
Would round to the nearest 1000.
Get the max number, then round it up to the nearest thousand?
<?php
$largest = max(125, 270, 517, 1680, 14790);
$nearest = ceil($largest / 1000) * 1000;
You could just loop through the numbers, remembering the highest value as you go. It's common coding practice. (Ok there really is a max() function as well, echo max(1, 3, 5, 6, 7); // 7)
Then you could divide the highest number by your factor, take the integer you get and add one to it, then multiply by the factor again and there you go.
just do : x=floor(14790/100) ; the floor function returns the next lowest integer value
For example, say I enter '10' for the amount of values, and '10000' as a total amount.
The script would need to randomize 10 different numbers that all equal up to 10000. No more, no less.
But it needs to be dynamic, as well. As in, sometimes I might enter '5' or '6' or even '99' for the amount of values, and any number (up to a billion or even higher) as the total amount.
How would I go about doing this?
EDIT: I should also mention that all numbers need to be a positive integer
The correct answer here is unbelievably simple.
Just imagine a white line, let's say 1000 units long.
You want to divide the line in to ten parts, using red marks.
VERY SIMPLY, CHOOSE NINE RANDOM NUMBERS and put a red paint mark at each of those points.
It's just that simple. You're done!
Thus, the algorithm is:
(1) pick nine random numbers between 0 and 1000
(2) put the nine numbers, a zero, and a 1000, in an array
(3) sort the array
(4) using subtraction get the ten "distances" between array values
You're done.
(Obviously if you want to have no zeros in your final set, in part (1) simply rechoose another random number if you get a collision.)
Ideally as programmers, we can "see" visual algorithms like this in our heads -- try to think visually whatever we do!
Footnote - for any non-programmers reading this, just to be clear pls note that this is like "the first thing you ever learn when studying computer science!" i.e. I do not get any credit for this, I just typed in the answer since I stumbled on the page. No kudos to me!
Just for the record another common approach (depending on the desired outcome, whether you're dealing with real or whole numbers, and other constraints) is also very "ah hah!" elegant. All you do is this: get 10 random numbers. Add them up. Remarkably simply, just: multiply or divide them all by some number, so that, the total is the desired total! It's that easy!
maybe something like this:
set max amount remaining to the target number
loop for 1 to the number of values you want - 1
get a random number from 0 to the max amount remaining
set new max amount remaining to old max amount remaining minus the current random number
repeat loop
you will end up with a 'remainder' so the last number is determined by whatever is left over to make up the original total.
Generate 10 random numbers till 10000 .
Sort them from big to small : g0 to g9
g0 = 10000 - r0
g1 = r0 - r1
...
g8 = r8 - r9
g9 = r9
This will yield 10 random numbers over the full range which add up to 10000.
I believe the answer provided by #JoeBlow is largely correct, but only if the 'randomness' desired requires uniform distribution. In a comment on that answer, #Artefacto said this:
It may be simple but it does not generate uniformly distributed numbers...
Itis biased in favor of numbers of size 1000/10 (for a sum of 1000 and 10 numbers).
This begs the question which was mentioned previously regarding the desired distribution of these numbers. JoeBlow's method does ensure a that element 1 has the same chance at being number x as element 2, which means that it must be biased towards numbers of size Max/n. Whether the OP wanted a more likely shot at a single element approaching Max or wanted a uniform distribution was not made clear in the question. [Apologies - I am not sure from a terminology perspective whether that makes a 'uniform distribution', so I refer to it in layman's terms only]
In all, it is incorrect to say that a 'random' list of elements is necessarily uniformly distributed. The missing element, as stated in other comments above, is the desired distribution.
To demonstrate this, I propose the following solution, which contains sequential random numbers of a random distribution pattern. Such a solution would be useful if the first element should have an equal chance at any number between 0-N, with each subsequent number having an equal chance at any number between 0-[Remaining Total]:
[Pseudo code]:
Create Array of size N
Create Integer of size Max
Loop through each element of N Except the last one
N(i) = RandomBetween (0, Max)
Max = Max - N(i)
End Loop
N(N) = Max
It may be necessary to take these elements and randomize their order after they have been created, depending on how they will be used [otherwise, the average size of each element decreases with each iteration].
Update: #Joe Blow has the perfect answer. My answer has the special feature of generating chunks of approximately the same size (or at least a difference no bigger than (10000 / 10)), leaving it in place for that reason.
The easiest and fastest approach that comes to my mind is:
Divide 10000 by 10 and store the values in an array. (10 times the value 10000)
Walk through every one of the 10 elements in a for loop.
From each element, subtract a random number between (10000 / 10).
Add that number to the following element.
This will give you a number of random values that, when added, will result in the end value (ignoring floating point issues).
Should be half-way easy to implement.
You'll reach PHP's maximum integer limit at some point, though. Not sure how far this can be used for values towards a billion and beyond.
Related: http://www.mathworks.cn/matlabcentral/newsreader/view_thread/141395
See this MATLAB package. It is accompanied with a file with the theory behind the implementation.
This function generates random, uniformly distributed vectors, x = [x1,x2,x3,...,xn]', which have a specified sum s, and for which we have a <= xi <= b, for specified values a and b. It is helpful to regard such vectors as points belonging to n-dimensional Euclidean space and lying in an n-1 dimensional hyperplane constrained to the sum s. Since, for all a and b, the problem can easily be rescaled to the case where a = 0 and b = 1, we will henceforth assume in this description that this is the case, and that we are operating within the unit n-dimensional "cube".
This is the implementation (© Roger Stafford):
function [x,v] = randfixedsum(n,m,s,a,b)
% Rescale to a unit cube: 0 <= x(i) <= 1
s = (s-n*a)/(b-a);
% Construct the transition probability table, t.
% t(i,j) will be utilized only in the region where j <= i + 1.
k = max(min(floor(s),n-1),0); % Must have 0 <= k <= n-1
s = max(min(s,k+1),k); % Must have k <= s <= k+1
s1 = s - [k:-1:k-n+1]; % s1 & s2 will never be negative
s2 = [k+n:-1:k+1] - s;
w = zeros(n,n+1); w(1,2) = realmax; % Scale for full 'double' range
t = zeros(n-1,n);
tiny = 2^(-1074); % The smallest positive matlab 'double' no.
for i = 2:n
tmp1 = w(i-1,2:i+1).*s1(1:i)/i;
tmp2 = w(i-1,1:i).*s2(n-i+1:n)/i;
w(i,2:i+1) = tmp1 + tmp2;
tmp3 = w(i,2:i+1) + tiny; % In case tmp1 & tmp2 are both 0,
tmp4 = (s2(n-i+1:n) > s1(1:i)); % then t is 0 on left & 1 on right
t(i-1,1:i) = (tmp2./tmp3).*tmp4 + (1-tmp1./tmp3).*(~tmp4);
end
% Derive the polytope volume v from the appropriate
% element in the bottom row of w.
v = n^(3/2)*(w(n,k+2)/realmax)*(b-a)^(n-1);
% Now compute the matrix x.
x = zeros(n,m);
if m == 0, return, end % If m is zero, quit with x = []
rt = rand(n-1,m); % For random selection of simplex type
rs = rand(n-1,m); % For random location within a simplex
s = repmat(s,1,m);
j = repmat(k+1,1,m); % For indexing in the t table
sm = zeros(1,m); pr = ones(1,m); % Start with sum zero & product 1
for i = n-1:-1:1 % Work backwards in the t table
e = (rt(n-i,:)<=t(i,j)); % Use rt to choose a transition
sx = rs(n-i,:).^(1/i); % Use rs to compute next simplex coord.
sm = sm + (1-sx).*pr.*s/(i+1); % Update sum
pr = sx.*pr; % Update product
x(n-i,:) = sm + pr.*e; % Calculate x using simplex coords.
s = s - e; j = j - e; % Transition adjustment
end
x(n,:) = sm + pr.*s; % Compute the last x
% Randomly permute the order in the columns of x and rescale.
rp = rand(n,m); % Use rp to carry out a matrix 'randperm'
[ig,p] = sort(rp); % The values placed in ig are ignored
x = (b-a)*x(p+repmat([0:n:n*(m-1)],n,1))+a; % Permute & rescale x
return