PHP slice off numbers after 4 non zero numbers in decimal - php

What I am trying to do is :
0.000000023455676554434 -> 0.0000002345
0.00000000000000000000002656565-> 0.00000000000000000000002656
0.012345 -> 0.01234
Code till now :
bcdiv(rtrim(sprintf('%.20f', $decimal), '0'),1,13);
The current code removes scientific notation, and trims any zeros towards the end if any, and cuts the decimals after 13 decimal points. but if the decimal is something like 0.023123123235435346 it would still show 13 decimal points while I am looking to get only 0.02312.
Any help wold be highly appreciated.

You can use base 10 log to get the number of zeros between the point and the first digit. This would do what you want:
$decimal = "0.00000000000000000000002656565";
echo bcdiv($decimal, 1, -1*floor(log($decimal, 10))-1+4);
Output: 0.00000000000000000000002656

I think preg_replace is also very easy to do. The value should be available as strings and not as float.
$trimValue = preg_replace('~(\d*\.0*[1-9]{4}).*~','$1',$value);
If you want 4 numbers after first non-zero number use this:
$trimValue = preg_replace('~(\d*\.0*[1-9]\d{3}).*~','$1',$value);
A complete test (First variant):
$data = [
'0.000000023455676554434', // -> 0.0000002345
'0.00000000000000000000002656565', //-> 0.00000000000000000000002656
'0.012345', // -> 0.01234
'0.023123123235435346', //0.02312
'0.00565',
];
$trimValues = [];
foreach($data as $value){
$trimValues[] = preg_replace('~(\d*\.0*[1-9]{4}).*~','$1',$value);
}
echo '<pre>';
var_export($trimValues);
Output:
array (
0 => '0.00000002345',
1 => '0.00000000000000000000002656',
2 => '0.01234',
3 => '0.02312',
4 => '0.00565',
)

Related

Looking for a PHP smart format number

I am looking for a solution for a smart number formatting in PHP.
For example we have 2 numbers below and we need 4 digits after decimal:
1.12345678
0.00002345678
Using normal number formatting, here are the results:
1.1234 // Looking good
0.0000 // No good
Can we make it keep going until there are 4 non-zero digits? If it can return 0.00002345, perfect!!!
Many thanks!!!
Might be overkill and the pattern could be optimized, but for fun; get optional 0s AND 4 NOT 0s after the .:
preg_match('/\d+\.([0]+)?[^0]{4}/', $num, $match);
echo $match[0];
To round it we can get 5 digits after the 0s and then format it to the length of that -1 (which will round):
preg_match('/\d+\.([0]+?[^0]{5})/', $num, $match);
echo number_format($match[0], strlen($match[1])-1);
For $num = '1234.000023456777'; the result will be 1,234.00002346 and the $matches will contain:
Array
(
[0] => 1234.000023456
[1] => 000023456
)
So this is the code I made to slove this:
$num = 0.00002345678;
$num_as_string = number_format($num,PHP_FLOAT_DIG,'.','');
$zeros = strspn($num_as_string, "0", strpos($num_as_string, ".")+1);
echo number_format($num, (4+$zeros), '.', '');
It converts the float number to a string, checks how many zeros exist after the decimal point and then does a number format with the extra zeros accounted for.
Note that it may break if your float is too big, you can change PHP_FLOAT_DIG to a number larger that may fix that.

PHP calculate integer of division

I would like to calculate an integer part of division. The numerator and denominator (especially their precision) should not be altered because it might change from one calculation to other, also the denominator is as an example, its integer part as well as decimal part might be different.
I tried to use floor, ceil, round but none of them produced a correct result.
Please see the code below, perhaps you'll spot the error:
<?php
$valueArr = [
// should return 1999
199.90,
199.92,
199.95,
199.97,
// should return 2000
200.00,
200.02,
200.05,
200.07,
// should return 2001
200.10,
200.12,
200.15,
200.17,
];
$denominator = 0.1;
$resultArr = [];
foreach ($valueArr as $value) {
$key = (string) $value;
$result = floor($value / $denominator);
$resultArr[$key] = $result;
}
echo "Denominator:\n";
var_dump($denominator);
echo "\n";
print_r($resultArr);
that gives result:
Denominator:
float(0.1)
Array
(
[199.9] => 1999
[199.92] => 1999
[199.95] => 1999
[199.97] => 1999
[200] => 2000
[200.02] => 2000
[200.05] => 2000
[200.07] => 2000
[200.1] => 2000
[200.12] => 2001
[200.15] => 2001
[200.17] => 2001
)
where:
[200.1] => 2000
is wrong because integer part of (200.1 / 0.1) is 2001.
Do you know how to produce correct result for the $valueArr as above? What did I do wrong?
I'm using PHP 7.3.8 (cli)
So there's two issues you're going to run into here. First is the lack of precision on floating points in general, and the second is PHP's automatically coercing your inputs into floating points before you have a chance to use something like bcdiv.
As such: First step is to store your input numbers as strings so you're not losing precision out of the gate by the parser interpreting them as floating point numbers. Then use bcdiv on them.
Since you're just after the integer portion and bcdiv returns a string on success, we can just drop the decimal part with string functions.
<?php
$valueArr = [
// should return 1999
'199.90',
'199.92',
'199.95',
'199.97',
// should return 2000
'200.00',
'200.02',
'200.05',
'200.07',
// should return 2001
'200.10',
'200.12',
'200.15',
'200.17',
'381736192374124241.294',
];
$denominator = '0.1';
$resultArr = [];
foreach ($valueArr as $value) {
$key = (string) $value;
$result = explode('.', bcdiv($value, $denominator))[0];
$resultArr[$key] = $result;
}
echo "Denominator:\n";
var_dump($denominator);
echo "\n";
print_r($resultArr);
And if you want to do something like rounding, ceil, floor with the output of bcdiv, check out this answer:
https://stackoverflow.com/a/51390451/395384
I get the right results using bcdiv().
$result = bcdiv($value,$denominator);
I always use BcMath, seems more reliable to me.
You did not do anything wrong. This is a problem with computers. It's difficult to represent float-point numbers accurately in a fixed space.
Try this
foreach ($valueArr as $v) {
$resultArr []= floor($v * (1 / $denominator));
}
My advice would be to try to convert division operation into multiplication.
In your case, division by 0.1 === multiplication by 10. So, use that.
In case you don't have the bcdiv that comes with the BcMath extenstion
you may use the sprintf() function to achieve a proper result with the floor() and without any problem even in a case the denominator is a float smaller than 0.0001.
Instead of:
$result = floor($value / $denominator);
use this:
$result = floor(sprintf('%f', $value / $denominator));
and you'll get the proper:
[200.1] => 2001

format number if not a whole number show with decimal other wise show whole number

Let's say I have 2 numbers like so
1.50
2.00
I want to format the number so that
1.50 will show as 1.5
2.00 will show as 2
Basically if it is not a whole number then show that with ending 0's removed and if it is a whole number to show whole. I was trying number_format('2.00', 2); but that of course keeps the decimals. I was hoping there was a easy way to do this.
Multiply the number with 1 and it will remove any trailing zeros.
$arr = ["1.50","2.00"];
foreach($arr as $v){
echo $v*1 . PHP_EOL;
}
//1.5
//2
Try casting both strings to floats:
echo (float)'1.50';
// => 1.5
echo (float)'2.00';
// => 2
Try it online!

Check where number 1 is in decimal number

I am having hundreds of numbers like:
0.00100000
0.01000000
0.01000000
1.00000000
0.00001000
0.00000100
I need to check where the number 1 is in those number, so basicly
1.00000000 = 1
0.10000000 = 2
0.01000000 = 3
I tried Round() function, but it sometimes prints numbers like 1.E-6 or something like that, I need exact location of number 1.
Thank you very much for help.
I wouldn't rely too much on the approach you posted in your answer. Use the following function instead:
function index_of_one($dec)
{
// maximum precision is 15
$str = str_replace('.','',sprintf('%.15f', $dec));
$pos = strpos($str, '1');
if ($pos === false) {
return -1;
}
return ($pos + 1);
}
Example:
$dec1 = 1.00000000;
$dec2 = 0.10000000;
$dec3 = 0.00010000;
echo index_of_one($dec1); // 1
echo index_of_one($dec2); // 2
echo index_of_one($dec3); // 5
Visit this link to test it.
Looks like I found a solution, used strpos() function which works perfectly!
Basicly:
strpos(0.01000000, 1)
I say using regex is suitable when micro-optimization is not top-priority, when it lessens total function calls, and when it provides a direct, desired result.
Considering your case and the sample input, preg_match_all('/[^.](?=[^1]*1)|1.*/',$number) does everything in one step. It matches each individual non-dot character that is later followed by a 1 OR from 1 to the end of the string.
(Regex Pattern Demo)
preg_match_all()'s return value is the number of matches, so it delivers exactly what you are asking for -- a count.
Effectively, dots are not counted nor are any of the characters after the first 1. If there is no 1 found, the count is 0 (not -1 as suggested by the other answer).
Here is a demonstration with a battery of tests (Demo):
$numbers=['0.00100000','0.10000000','0.01000000','1.00000000','0.00001000','0.00000100','0.00000000'];
foreach($numbers as $number){
echo "$number = ",preg_match_all('/[^.](?=[^1]*1)|1.*/',$number),"\n";
}
Output:
0.00100000 = 4
0.10000000 = 2
0.01000000 = 3
1.00000000 = 1
0.00001000 = 6
0.00000100 = 7
0.00000000 = 0

php placement of the dot in a long number

I want to display a large number with a leading zero and a dot after.
The balance i want to display starts with 0.000000000000000000 ( 18 zeros after the dot ). This should be able to go up to 99.00000000000000000.( 17 zeros after the dot ).
I did a lot of trial and error but i just can't seem to get the dot in there. As far as for the zeros i got it working. What i have now is:
$leadingBalance = sprintf("%019d", $balance);
echo $leadingBalance;
This will display the correct balance but i need to place the dot in there. It means that if my balance has 17 or 18 numbers it should place the dot as 0.0000... If the balance has 19 numbers it should place the dot as 00.0000...
Whatever i try, how much i look up i can't figure it out.
For eg:
$n1 = 0;
$n2 = 99;
echo number_format($n1,18)."<br>";
echo number_format($n2,18)."<br>";
See the documentation for number_format: http://php.net/number_format
The functions parameters are:
string number_format ( float $number , int $decimals = 0 , string $dec_point = '.' , string $thousands_sep = ',' )
So use:
number_format(1000000000000000, 2, '.', '');
Which means that you don't use any (= empty string) thousands separator, only a decimal point.
or if you just want padding of 19 zero after decimal point
just use
sprintf("%0.19f",$number);
or else
if u want a number always 20 digit without caring about whole no and decimal value than use str_pad()
eg:
$no = sprintf("%0.2f",100); //100.00
this will convert your no to decimal point with 2 digit after decimal now just pad some digit if require to make it 20 digit long
echo str_pad($no,20,"0"); //100.00(15 zero after this)
this will check no of digit available and pad 0 to make it 20 digit
for more ref:https://www.w3schools.com/php/func_string_number_format.asp
You are using %019d when you actually wants a float number, try this:
<?php
$format = '%0.19f';
$args = 9;
$result = sprintf ($format, $args);
//$result will be equal to 9.0000000000000000000
?>
<?php
$format = '%0.19f';
$args = 99;
$result = sprintf ($format, $args);
//$result will be equal to 99.0000000000000000000
?>
Edit:
Since it looks like you want your whole number to be equal to 20 digits only, you may try to do some math if number_format doesn't do your job. You can try something like:
<?php
$number = 999;
$number_length = strlen($number);
$format_len = 20 - $number_length;
$format = '%0.'. $format_len .'f';
$result = sprintf($format, $number);
?>

Categories