I'm displaying how large a list of files are in a table using PHP. I'd like to display how big they are in megabytes instead of the default bytes. The problem I'm having is that I get extremely long decimals, which is impractical for this purpose.
Here's what I have so far:
print((filesize("../uploads/" . $dirArray[$index])) * .000000953674316 . " MB");
Which correctly converts the value, but changes, for example, 71 B to 6.7710876436E-5 MB.
I think the E-5 thing is like x10^-5 which would probably add up correctly, but is there a way I can cut off how many numbers it goes down to? If it displays as "00.00 MB" that's fine by me, most file are going to be much bigger than this test one.
You can format numbers with the number_format() function.
Edit: the manual page contains a user comment you may like: http://es.php.net/manual/en/function.number-format.php#72969
With good old printf :
printf("%.2f MB",filesize("../uploads/" . $dirArray[$index]) * .000000953674316);
Maybe, because it's a bit clearer what the intention is:
printf("%.2f MB",filesize("../uploads/" . $dirArray[$index]) / (1024 * 1024));
number_format() is good, and don't forget that round() can round the number to any precision you want.
Here is simple nice function: Quick PHP
If you need other units, too, you might use this function I wrote years ago:
<?php
function human_filesize($size, $precision = 2)
{
$a_size = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
$count = count($a_size) - 1;
$i = 0;
while ($size / 1024 >= 1 && $count >= $i) {
$size = $size / 1024;
$i++;
}
return round($size, $precision) . ' ' . $a_size[$i];
}
// =========
// USAGE
// =========
// Output: 34.35 MiB
echo human_filesize(filesize('file.zip'));
// Output: 34 MiB
echo human_filesize(filesize('file.zip'), 0);
// Output: 34.35465 MiB
echo human_filesize(filesize('file.zip'), 5);
?>
Related
How does one generate a random float between 0 and 1 in PHP?
I'm looking for the PHP's equivalent to Java's Math.random().
You may use the standard function: lcg_value().
Here's another function given on the rand() docs:
// auxiliary function
// returns random number with flat distribution from 0 to 1
function random_0_1()
{
return (float)rand() / (float)getrandmax();
}
Example from documentation :
function random_float ($min,$max) {
return ($min+lcg_value()*(abs($max-$min)));
}
rand(0,1000)/1000 returns:
0.348 0.716 0.251 0.459 0.893 0.867 0.058 0.955 0.644 0.246 0.292
or use a bigger number if you want more digits after decimal point
class SomeHelper
{
/**
* Generate random float number.
*
* #param float|int $min
* #param float|int $max
* #return float
*/
public static function rand($min = 0, $max = 1)
{
return ($min + ($max - $min) * (mt_rand() / mt_getrandmax()));
}
}
update:
forget this answer it doesnt work wit php -v > 5.3
What about
floatVal('0.'.rand(1, 9));
?
this works perfect for me, and it´s not only for 0 - 1 for example between 1.0 - 15.0
floatVal(rand(1, 15).'.'.rand(1, 9));
function mt_rand_float($min, $max, $countZero = '0') {
$countZero = +('1'.$countZero);
$min = floor($min*$countZero);
$max = floor($max*$countZero);
$rand = mt_rand($min, $max) / $countZero;
return $rand;
}
example:
echo mt_rand_float(0, 1);
result: 0.2
echo mt_rand_float(3.2, 3.23, '000');
result: 3.219
echo mt_rand_float(1, 5, '00');
result: 4.52
echo mt_rand_float(0.56789, 1, '00');
result: 0.69
$random_number = rand(1,10).".".rand(1,9);
function frand($min, $max, $decimals = 0) {
$scale = pow(10, $decimals);
return mt_rand($min * $scale, $max * $scale) / $scale;
}
echo "frand(0, 10, 2) = " . frand(0, 10, 2) . "\n";
This question asks for a value from 0 to 1. For most mathematical purposes this is usually invalid albeit to the smallest possible degree. The standard distribution by convention is 0 >= N < 1. You should consider if you really want something inclusive of 1.
Many things that do this absent minded have a one in a couple billion result of an anomalous result. This becomes obvious if you think about performing the operation backwards.
(int)(random_float() * 10) would return a value from 0 to 9 with an equal chance of each value. If in one in a billion times it can return 1 then very rarely it will return 10 instead.
Some people would fix this after the fact (to decide that 10 should be 9). Multiplying it by 2 should give around a ~50% chance of 0 or 1 but will also have a ~0.000000000465% chance of returning a 2 like in Bender's dream.
Saying 0 to 1 as a float might be a bit like mistakenly saying 0 to 10 instead of 0 to 9 as ints when you want ten values starting at zero. In this case because of the broad range of possible float values then it's more like accidentally saying 0 to 1000000000 instead of 0 to 999999999.
With 64bit it's exceedingly rare to overflow but in this case some random functions are 32bit internally so it's not no implausible for that one in two and a half billion chance to occur.
The standard solutions would instead want to be like this:
mt_rand() / (getrandmax() + 1)
There can also be small usually insignificant differences in distribution, for example between 0 to 9 then you might find 0 is slightly more likely than 9 due to precision but this will typically be in the billionth or so and is not as severe as the above issue because the above issue can produce an invalid unexpected out of bounds figure for a calculation that would otherwise be flawless.
Java's Math.random will also never produce a value of 1. Some of this comes from that it is a mouthful to explain specifically what it does. It returns a value from 0 to less than one. It's Zeno's arrow, it never reaches 1. This isn't something someone would conventionally say. Instead people tend to say between 0 and 1 or from 0 to 1 but those are false.
This is somewhat a source of amusement in bug reports. For example, any PHP code using lcg_value without consideration for this may glitch approximately one in a couple billion times if it holds true to its documentation but that makes it painfully difficult to faithfully reproduce.
This kind of off by one error is one of the common sources of "Just turn it off and on again." issues typically encountered in embedded devices.
Solution for PHP 7. Generates random number in [0,1). i.e. includes 0 and excludes 1.
function random_float() {
return random_int(0, 2**53-1) / (2**53);
}
Thanks to Nommyde in the comments for pointing out my bug.
>>> number_format((2**53-1)/2**53,100)
=> "0.9999999999999998889776975374843459576368331909179687500000000000000000000000000000000000000000000000"
>>> number_format((2**53)/(2**53+1),100)
=> "1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
Most answers are using mt_rand. However, mt_getrandmax() usually returns only 2147483647. That means you only have 31 bits of information, while a double has a mantissa with 52 bits, which means there is a density of at least 2^53 for the numbers between 0 and 1.
This more complicated approach will get you a finer distribution:
function rand_754_01() {
// Generate 64 random bits (8 bytes)
$entropy = openssl_random_pseudo_bytes(8);
// Create a string of 12 '0' bits and 52 '1' bits.
$x = 0x000FFFFFFFFFFFFF;
$first12 = pack("Q", $x);
// Set the first 12 bits to 0 in the random string.
$y = $entropy & $first12;
// Now set the first 12 bits to be 0[exponent], where exponent is randomly chosen between 1 and 1022.
// Here $e has a probability of 0.5 to be 1022, 0.25 to be 1021, etc.
$e = 1022;
while($e > 1) {
if(mt_rand(0,1) == 0) {
break;
} else {
--$e;
}
}
// Pack the exponent properly (add four '0' bits behind it and 49 more in front)
$z = "\0\0\0\0\0\0" . pack("S", $e << 4);
// Now convert to a double.
return unpack("d", $y | $z)[1];
}
Please note that the above code only works on 64-bit machines with a Litte-Endian byte order and Intel-style IEEE754 representation. (x64-compatible computers will have this). Unfortunately PHP does not allow bit-shifting past int32-sized boundaries, so you have to write a separate function for Big-Endian.
You should replace this line:
$z = "\0\0\0\0\0\0" . pack("S", $e << 4);
with its big-endian counterpart:
$z = pack("S", $e << 4) . "\0\0\0\0\0\0";
The difference is only notable when the function is called a large amount of times: 10^9 or more.
Testing if this works
It should be obvious that the mantissa follows a nice uniform distribution approximation, but it's less obvious that a sum of a large amount of such distributions (each with cumulatively halved chance and amplitude) is uniform.
Running:
function randomNumbers() {
$f = 0.0;
for($i = 0; $i < 1000000; ++$i) {
$f += \math::rand_754_01();
}
echo $f / 1000000;
}
Produces an output of 0.49999928273099 (or a similar number close to 0.5).
I found the answer on PHP.net
<?php
function randomFloat($min = 0, $max = 1) {
return $min + mt_rand() / mt_getrandmax() * ($max - $min);
}
var_dump(randomFloat());
var_dump(randomFloat(2, 20));
?>
float(0.91601131712832)
float(16.511210331931)
So you could do
randomFloat(0,1);
or simple
mt_rand() / mt_getrandmax() * 1;
what about:
echo (float)('0.' . rand(0,99999));
would probably work fine... hope it helps you.
This question already has answers here:
Format bytes to kilobytes, megabytes, gigabytes
(28 answers)
Closed 3 years ago.
After thinking mine was in error,
I found LOT AT LOTS of scripts the do this:
https://gist.github.com/liunian/9338301
And there are several here at S.O. I used, but had the same annoying "B" as a size.
This issue seemed to rear it's ugly head when I switched to php v7.xxx
First issues is I have to typcase a floated number (or double) or else I get a
"A non well formed numeric value encountered"
After some research, apparently this is NOT a bug. At least that is how I read it.
So after typcasting it, the error goes away but the value returned is always a "B'
filesize = 87.5B (when it should be MB or GB).
I am pretty sure Javascript will work, but would rather keep it with php.
Thanks for looking
current live script that is producing a "B" only
public function readableBytes($size, $type='pc') { //ignore the pc - it is for something else - disabled for debugging
$size = (double)$size;
static $units = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
$step = 1024;
$i = 0;
while (($size / $step) > 0.9) {
$size = $size / $step;
$i++;
}
return round($size, 2).$units[$i];
}// function readbbleBytes
This is a unique issue to those that use a loop for assigning (custom)template tags with an array field.
The filesize is one of many fields in an array I use.
I used a loop to go through each field and assign it to a template tag.
Not sure why the "B" came up. My suspicion is that the result of a "string" = 0 bytes. Even though It showed the actual size.
edit: spelling & claification
So to fix, in the middle of the loop, I forced the $array['filesize'] = readableBytes($array['filesize'])).
Before it was loop tag = fieldname.
foreach ($arr as $field=>$data) {
$arr['filesize'] = readableBytes($array['filesize'])); // fix was put here
$page = str_ireplace("{$field}", $data, $page);
}
The following seems to work (checked in phptester):
function human_filesize($bytes, $decimals = 2)
{
if ($bytes < 1024) {
return $bytes . ' B';
}
$factor = floor(log($bytes, 1024));
return sprintf("%.{$decimals}f ", $bytes / pow(1024, $factor)) . ['B', 'KB', 'MB', 'GB', 'TB', 'PB'][$factor];
}
This is a cut and paste of a post by gladx in the following thread, where you'll find several optimised examples: https://gist.github.com/liunian/9338301#gistcomment-2183132
In my code I am outputting a number from a database, the number is in MB.
So for example I am getting the HDD size and free space.
Looks like this:
C: CAP 1141919
C: FREE 487205
What I am trying to do is convert those number to either TB or GB
The output would look like:
C: CAP 1.141919TB
C: FREE 487.205GB
Further more I want to cut the input down to two decimal places.
C: CAP 1.14TB
C: FREE 487.20GB
I have tried a couple different functions however most of them want the number in bytes, which I did find a way to convert the number from MB to B then to GB but I ran into a problem with the TB, also im sure there is a way to do this in one step.
Any help would be appreciated.
function convert($size)
{
$filesizename = array(" MB", " GB", " TB");
$size = round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . $filesizename[$i];
return $size;
}
$test = convert(4000);
echo $test; //produces 3.91 GB
another variant:
function convertMB($mb) {
$mb = floatval($mb) / 1024.0;
return ($mb < 1024) ? (number_format($mb, 2).'GB') : (number_format($mb / 1024.0, 2).'TB');
}
Set decimal points by number_format function:
$number = number_format($number, 2); // 2 is decimal points
I tried to play with php,however I got stuck at one place, where I tested value of $n=1024, then it takes more than 60sec,so timeout error of php arises,I don't know how to overcome this problem,if my only requirement is to present any input number in the 20 + ---+ 2n Form.
trying below code with n=121,I got this,but I wish to represent 57 also in 2n
Form,So I tried recursion,which didn't worked.
see how a given no. be represented in powers of '2': 20 + 21 + 22 +
2 + 24+ 25 + 26+ 57
CODE:
<?php
echo("see how a given no. be represented in powers of '2' :<br/>\n");
$n=121;
$two_pow=array(
pow(2,0),pow(2,1),pow(2,2),pow(2,3),pow(2,4),pow(2,5),
pow(2,6),pow(2,7),pow(2,8),pow(2,9),pow(2,10)
);
//print_r($two_pow);
$i=0;
while($n>=$two_pow[$i])
$i++;
/* displaying 2^3*/
if($i>0)
$ij=$i-1;
/* diplaying difference of give N and 2^i*/
$diff=$n-$two_pow[$ij];
if($n>0)
{
for($i=0;$i<=$ij;$i++)
{
echo("2<sup> $i </sup>"."+ \n");
if($i==$ij && $diff>0)
{
echo("\n". $diff);
}
}
}
else
echo("<br/>not possible for values less then zero");
?>
No need for recursion or anything like that, just convert to binary and loop through the characters:
$bits = array_reverse(str_split(decbin($n)));
$output = array();
foreach($bits as $key => $bit) {
if($bit == 1) {
$output[] = '2<sup>'.($key).'</sup>';
}
}
echo implode(' + ', $output);
Working example:
http://codepad.org/plzvw2RL
Cant you use - base_convert() to convert the string to binary, then format your output based on the position of bits?
It is a joke right?
Oh it isn't?
Well ok
take look at decbin function. Isn't it easier?
You can overcome the timeout restriction by disabling the timeout:
set_time_limit(0);
my question is: is there a good (common) algorithm to create numbers, which match well looking user understood numbers out of incomming (kind of random looking for a user) numbers.
i.e. you have an interval from
130'777.12 - 542'441.17.
But for the user you want to display something more ...say userfriendly, like:
130'000 - 550'000.
how can you do this for several dimensions?
an other example would be:
23.07 - 103.50 to 20 - 150
do you understand what i mean?
i should give some criteria as well:
the interval min and max should
include the given limits.
the "rounding" should be in a
granularity which reflects the
distance between min and max (meaning
in our second example 20 - 200
would be too coarse)
very much honor you'll earn if you know a native php function which can do this :-)
*update - 2011-02-21 *
I like the answer from #Ivan and so accepted it. Here is my solution so far:
maybe you can do it better. i am open for any proposals ;-).
/**
* formats a given float number to a well readable number for human beings
* #author helle + ivan + greg
* #param float $number
* #param boolean $min regulates wheter its the min or max of an interval
* #return integer
*/
function pretty_number($number, $min){
$orig = $number;
$digit_count = floor(log($number,10))+1; //capture count of digits in number (ignoring decimals)
switch($digit_count){
case 0: $number = 0; break;
case 1:
case 2: $number = round($number/10) * 10; break;
default: $number = round($number, (-1*($digit_count -2 )) ); break;
}
//be sure to include the interval borders
if($min == true && $number > $orig){
return pretty_number($orig - pow(10, $digit_count-2)/2, true);
}
if($min == false && $number < $orig){
return pretty_number($orig + pow(10, $digit_count-2)/2, false);
}
return $number;
}
I would use Log10 to find how "long" the number is and then round it up or down. Here's a quick and dirty example.
echo prettyFloor(23.07);//20
echo " - ";
echo prettyCeil(103.50);//110
echo prettyFloor(130777.12);//130000
echo " - ";
echo prettyCeil(542441.17);//550000
function prettyFloor($n)
{
$l = floor(log(abs($n),10))-1; // $l = how many digits we will have to nullify :)
if ($l<=0)
$l++;
if ($l>0)
$n=$n/(pow(10,$l)); //moving decimal point $l positions to the left eg(if $l=2 1234 => 12.34 )
$n=floor($n);
if ($l>0)
$n=$n*(pow(10,$l)); //moving decimal point $l positions to the right eg(if $l=2 12.3 => 1230 )
return $n;
}
function prettyCeil($n)
{
$l = floor(log(abs($n),10))-1;
if ($l<=0)
$l++;
if ($l>0)
$n=$n/(pow(10,$l));
$n=ceil($n);
if ($l>0)
$n=$n*(pow(10,$l));
return $n;
}
This example unfortunately will not convert 130 to 150. As both 130 and 150 have the same precision. Even thou for us, humans 150 looks a bit "rounder". In order to achieve such result I would recommend to use quinary system instead of decimal.
You can use php's round function which takes a parameter to specify the precision.
<?php
echo round(3.4); // 3
echo round(3.5); // 4
echo round(3.6); // 4
echo round(3.6, 0); // 4
echo round(1.95583, 2); // 1.96
echo round(1241757, -3); // 1242000
echo round(5.045, 2); // 5.05
echo round(5.055, 2); // 5.06
?>
The number_format() function handles "prettifying" numbers with arbitrary thousands/decimal characters and decimal places, but you'd have to split your ranges/strings into individual numbers, as number_formation only works on one number at a time.
The rounding portion would have to handled seperately as well.
I haven't seen ready algorithm or function for that. But it should be simple, based on string replacement (str_replace, preg_replace), number_format and round functions.
This actually is kind of a special case, that can be addressed with the following function:
function roundto($val, $toceil=false) {
$precision=2; // try 1, 2, 5, 10
$pow = floor(log($val, 10));
$mult = pow(10, $pow);
$a = $val/$mult*$precision;
if (!$toceil) $a-=0.5; else $a+=0.5;
return round($a)/$precision*$mult;
}
$v0=130777.12; $v1=542441.17;
echo number_format(roundto($v0, false), 0, '.', "'").' - '
.number_format(roundto($v1, true), 0, '.', "'").'<br/>';
$v0=23.07; $v1=103.50;
echo number_format(roundto($v0, false), 0, '.', "'").' - '
.number_format(roundto($v1, true), 0, '.', "'").'<br/>';
Outputs exactly this:
100'000 - 550'000
20 - 150
For any other case of number formatting it might be interesting to have a look at my newly published PHP class "php-beautiful-numbers", which I use in almost ever project to display run times ("98.4 µs" [= 9.8437291615846E-5]) or numbers in running text (e.g. "you booked two flights." [= 2]).
https://github.com/SirDagen/php-beautiful-numbers