# Rounding to nearest fraction (half, quarter, etc.)

Viewed   85 times

So, I need to create the following functions but my head can't think of any possibility in PHP without complicated math.

• Round always up to the nearest decimal (1.81 = 1.90, 1.89 = 1.90, 1.85 = 1.90)
• Round always down to the nearest decimal (1.81 = 1.80, 1.89 = 1.80, 1.85 = 1.80)
• Round always up to the nearest x.25 / x.50 / x.75 / x.00 (1.81 = 2, 1.32 = 1.50)
• Round always down to the nearest x.25 / x.50 / x.75 / x.00 (1.81 = 1.75, 1.32 = 1.25)
• Round always up to the nearest x.50 / 1 (1.23 = 1.50, 1.83 = 2)
• Round always down to the nearest x.50 / 1 (1.23 = 1, 1.83 = 1.50)

I have searched on Google for 2 hours now and the only things that came up were Excel forums. Is it possible with some simple lines of PHP?

1

Since you're looking for fourths (`.00`, `.25`, `.50`, `.75`), multiply your number by 4, round to nearest whole number as desired (`floor` if down, `ceil` if up), then divide by 4.

1.32, down to nearest fourth:

1.32 * 4 = 5.28
floor(5.28) = 5.00
5.00 / 4 = 1.25

Same principle applies for any other fractions, such as thirds or eighths (`.0`, `.125`, `.25`, `.375`, `.5`, `.625`, `.75`, `.875`). For example:

1.77, up to nearest eighth:

1.77 * 8 = 14.16
ceil(14.16) = 15.00
15.00 / 8 = 1.875

Just for fun, you could write a function like this:

``````function floorToFraction(\$number, \$denominator = 1)
{
\$x = \$number * \$denominator;
\$x = floor(\$x);
\$x = \$x / \$denominator;
return \$x;
}

echo floorToFraction(1.82);      // 1
echo floorToFraction(1.82, 2);   // 1.5
echo floorToFraction(1.82, 3);   // 1.6666666666667
echo floorToFraction(1.82, 4);   // 1.75
echo floorToFraction(1.82, 9);   // 1.7777777777778
echo floorToFraction(1.82, 25);  // 1.8
``````
Wednesday, October 12, 2022

4

After a night lost trying to solve this problem I believe I've found a rather simple solution, here it is:

``````function bcceil(\$number)
{
if (strpos(\$number, '.') !== false) {
if (preg_match("~.+\$~", \$number)) return bcround(\$number, 0);
if (\$number != '-') return bcadd(\$number, 1, 0);
return bcsub(\$number, 0, 0);
}
return \$number;
}

function bcfloor(\$number)
{
if (strpos(\$number, '.') !== false) {
if (preg_match("~.+\$~", \$number)) return bcround(\$number, 0);
if (\$number != '-') return bcadd(\$number, 0, 0);
return bcsub(\$number, 1, 0);
}
return \$number;
}

function bcround(\$number, \$precision = 0)
{
if (strpos(\$number, '.') !== false) {
if (\$number != '-') return bcadd(\$number, '0.' . str_repeat('0', \$precision) . '5', \$precision);
return bcsub(\$number, '0.' . str_repeat('0', \$precision) . '5', \$precision);
}
return \$number;
}
``````

I think I didn't miss anything, if someone can spot any bug please let me know. Here are some tests:

``````assert(bcceil('4') == ceil('4')); // true
assert(bcceil('4.3') == ceil('4.3')); // true
assert(bcceil('9.999') == ceil('9.999')); // true
assert(bcceil('-3.14') == ceil('-3.14')); // true

assert(bcfloor('4') == floor('4')); // true
assert(bcfloor('4.3') == floor('4.3')); // true
assert(bcfloor('9.999') == floor('9.999')); // true
assert(bcfloor('-3.14') == floor('-3.14')); // true

assert(bcround('3', 0) == number_format('3', 0)); // true
assert(bcround('3.4', 0) == number_format('3.4', 0)); // true
assert(bcround('3.5', 0) == number_format('3.5', 0)); // true
assert(bcround('3.6', 0) == number_format('3.6', 0)); // true
assert(bcround('1.95583', 2) == number_format('1.95583', 2)); // true
assert(bcround('5.045', 2) == number_format('5.045', 2)); // true
assert(bcround('5.055', 2) == number_format('5.055', 2)); // true
assert(bcround('9.999', 2) == number_format('9.999', 2)); // true
``````
Monday, September 26, 2022

3

This is because the number 1111111.505 can't be represented exactly in floating point notation. The closest it can get is 1111111.5049999999. So what ends up happening is that it converts the number in your code to 1111111.50499999999 and then it does the rounding. Which results in 1111111.5. Floating point numbers have problems in that they can't represent a lot of even seemingly simple decimal numbers with complete accuracy. For instance. the number 0.1 cannot be accurately represented using binary floating numbers. Using Python, if you type in 0.1, it returns 0.10000000000001. Plus or minus a few zeros. This is the reason that some languages such as .Net provide a "Decimal" data type which is able to represent all decimal values within a certain range and number of decimal places. The downside of the decimal data type is that it is slower, and each number takes more space to store.

Tuesday, November 15, 2022

5

One option for doing this would be as follows:

1. Multiply the value by 20.
2. Use `Math.round` to round to the nearest integer.
3. Divide by 20 again.

For example:

``````double rounded = Math.round(x * 20.0) / 20.0;
``````

Hope this helps!

Monday, November 14, 2022

5

I'd try something like the following:

``````>>> from decimal import Decimal, ROUND_HALF_UP
>>> x = Decimal('2.47')
>>> x.quantize(Decimal('1'), rounding=ROUND_HALF_UP).quantize(Decimal('0.01'))
Decimal('2.00')
``````

The key part here is the first call: `x.quantize(Decimal('1'), rounding=ROUND_HALF_UP)` gives `x` rounded to the nearest integer, with the given rounding mode. The first argument (`Decimal('1')`) determines the exponent of the rounded result, so if you replaced it with e.g., `Decimal('0.1')` it would round to the nearest tenth, instead, and if you replaced it with `Decimal('1e1')` it would round to the nearest multiple of `10`.

Then the second `quantize` call just puts the extra two decimal places back in so that you get `Decimal('2.00')` coming out instead of just `Decimal(2)`.

You could also use the `to_integral_value` method in place of the first quantize call, if you want:

``````>>> x.to_integral_value(rounding=ROUND_HALF_UP).quantize(Decimal('0.01'))
Decimal('2.00')
``````

I can't see any strong reason to prefer either solution over the other.

Sunday, October 23, 2022