223

Scenario: the size of various files are stored in a database as bytes. What's the best way to format this size info to kilobytes, megabytes and gigabytes? For instance I have an MP3 that Ubuntu displays as "5.2 MB (5445632 bytes)". How would I display this on a web page as "5.2 MB" AND have files less than one megabyte display as KB and files one gigabyte and above display as GB?

Prix
  • 19,417
  • 15
  • 73
  • 132
leepowers
  • 37,828
  • 23
  • 98
  • 129
  • 5
    I belive you should create a function doing this. Just divide number by 1024 and look at result. If its more then 1024 then divide again. – Ivan Nevostruev Mar 24 '10 at 18:37

28 Answers28

369
function formatBytes($bytes, $precision = 2) { 
    $units = array('B', 'KB', 'MB', 'GB', 'TB'); 
   
    $bytes = max($bytes, 0); 
    $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); 
    $pow = min($pow, count($units) - 1); 
   
    // Uncomment one of the following alternatives
    // $bytes /= pow(1024, $pow);
    // $bytes /= (1 << (10 * $pow)); 
   
    return round($bytes, $precision) . $units[$pow]; 
} 

(Taken from php.net, there are many other examples there, but I like this one best :-)

hakre
  • 193,403
  • 52
  • 435
  • 836
Leo
  • 37,640
  • 8
  • 75
  • 100
  • I almost upvoted your answer (it's very similar to mine), but I don't like this "division by 1024^$pow" business. You should just do 1024^fraction($pow) and be done. :-) – C. K. Young Mar 24 '10 at 18:40
  • 8
    If you used `$bytes /= (1 << (10 * $pow))` or the like, I could like it better. :-P – C. K. Young Mar 24 '10 at 18:46
  • 5
    There you go :D (personally, I don't like bitwise arithmetic, because it is hard to understand if you aren't used to it...) – Leo Mar 24 '10 at 18:50
  • `9287695` returns `8.86 MB` with a precision of 2. That doesn't seem right. – Justin Sep 01 '12 at 09:02
  • 3
    @Justin that's because 9287695 / 1024 / 1024 is indeed 8,857 :) – Mahn Sep 27 '12 at 20:26
  • 45
    Actually, it's `KiB`, `MiB`, `GiB` and `TiB` since you are dividing by `1024`. If you divided by `1000` it would be without the `i`. – Devator May 16 '13 at 19:41
  • 1
    `$bytes /= (1 << (10 * $pow))` does not work well but number above 1 TB – David Bélanger Aug 05 '13 at 16:40
  • 1
    Just FYI, the second alternative above calculates TB wrong as David Belanger also pointed out. Use the first alternative instead ($bytes /= pow(1024, $pow);) – fred Jun 03 '14 at 15:59
  • 1
    use return round($bytes, $precision) . ' ' . $units[$pow]; if you never want the unit being pushed onto the next line in HTML output. – Rid Iculous Jun 07 '16 at 01:44
  • 12
    `Uncomment one of the following alternatives` was something I did not notice for 5 minutes ... – Arnis Juraga Aug 08 '17 at 16:04
  • Why is `$bytes = max($bytes, 0);` used here? – yogur Aug 21 '17 at 06:50
  • 2
    @yogur, it is so that if $bytes is a negative value, it will set it to 0 instead. – kojow7 Nov 19 '17 at 21:15
  • 1
    Do not forget to uncomment one of the two lines stated in the code. – TheTechGuy Oct 01 '19 at 17:07
  • `return round($bytes, $precision) . ' ' . $units[$pow]; ` needs to be `return round($pow, $precision) . ' ' . $units[$pow]; `. Isn't it? check:- https://3v4l.org/3GEig AND https://3v4l.org/gqE3D – Alive to die - Anant Mar 18 '20 at 07:10
  • if you are working with WordPress, you can use the shared function `size_format()`. – Earlee Dec 04 '22 at 10:52
  • Does not work out of the box (just tried copy-pasting without checking comments). I would ask the author to adapt the code so that it does (uncomment one of the two options). Until then I recommend this answer: https://stackoverflow.com/a/2510540/1240678 – Tyreal Apr 24 '23 at 14:24
243

This is Chris Jester-Young's implementation, cleanest I've ever seen, combined with php.net's and a precision argument.

function formatBytes($size, $precision = 2)
{
    $base = log($size, 1024);
    $suffixes = array('', 'K', 'M', 'G', 'T');   

    return round(pow(1024, $base - floor($base)), $precision) .' '. $suffixes[floor($base)];
}

echo formatBytes(24962496);
// 23.81M

echo formatBytes(24962496, 0);
// 24M

echo formatBytes(24962496, 4);
// 23.8061M
shasi kanth
  • 6,987
  • 24
  • 106
  • 158
John Himmelman
  • 21,504
  • 22
  • 65
  • 80
  • 8
    it has 2 errors - add 1 to (at least small) files size - not working with 0 (return NAN) – maazza Aug 31 '12 at 10:35
  • Nice one. Is there a version of this the other way around? – Luke Jul 04 '13 at 00:43
  • 3
    *a lil dreaming* **:** `$suffixes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');` I wants a Yottabyte hard drive! ***:-P*** – SpYk3HH Dec 17 '13 at 16:49
  • 1
    i had to cast the $size to a double to get it to work. heres what worked for me: function formatBytes($size, $precision = 2){ $base = log(floatval($size)) / log(1024); $suffixes = array('', 'k', 'M', 'G', 'T'); return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)]; } – CGray Mar 19 '14 at 17:02
  • `formatBytes(259748192, 3)` returns `259748192 MB` which is not right – Flip Jun 14 '14 at 23:13
  • For the pedants amongst us (isn't that all of us?): 'K' should be 'k'. See: https://en.wikipedia.org/wiki/Metric_prefix. – darrenp Oct 12 '16 at 20:07
  • 1
    I like this too, and think it's clean, but a little more explaination about what this function is _doing_ in steps would be appreciated, for us noobs :). For example: why hard code `1024` instead of [using diff converters](http://www.eagle-web-designs.com/cool_stuff/ByteConversion.html) like `$unitInBytes = 1 * pow(2, 10)` then doing a `return ($size / $unitInBytes)` or some combination thereof, why does this method produce better results? Any clarification is appreciated for understanding it, rather than just copy/pasting and never thinking about it again. – mrClean Feb 20 '17 at 21:17
  • It calculates bits $suffixes = array('', 'K', 'M', 'G', 'T'); but if you want to show only minimum in Megabytes remove first 2 values $suffixes = array('M', 'G', 'T'); now calculate echo formatBytes(1024, 0); it will come up with 1GB – shaz3e Jun 23 '17 at 22:25
  • IDE looks nicer when added (int) here: return .......$suffixes[(int)floor($base)]; – Vit Aug 23 '21 at 09:50
97

Pseudocode:

$base = log($size) / log(1024);
$suffix = array("", "k", "M", "G", "T")[floor($base)];
return pow(1024, $base - floor($base)) . $suffix;
C. K. Young
  • 219,335
  • 46
  • 382
  • 435
18

Just divide it by 1024 for kb, 1024^2 for mb and 1024^3 for GB. As simple as that.

Vonder
  • 4,033
  • 15
  • 44
  • 61
  • 1
    Downvoted, because a) it's factually wrong (see ISO 80000-13), b) doesn't provide any real solution in the form of a piece of code. – ericek111 Sep 20 '22 at 08:29
16

This is Kohana's implementation, you could use it:

public static function bytes($bytes, $force_unit = NULL, $format = NULL, $si = TRUE)
{
    // Format string
    $format = ($format === NULL) ? '%01.2f %s' : (string) $format;

    // IEC prefixes (binary)
    if ($si == FALSE OR strpos($force_unit, 'i') !== FALSE)
    {
        $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
        $mod   = 1024;
    }
    // SI prefixes (decimal)
    else
    {
        $units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB');
        $mod   = 1000;
    }

    // Determine unit to use
    if (($power = array_search((string) $force_unit, $units)) === FALSE)
    {
        $power = ($bytes > 0) ? floor(log($bytes, $mod)) : 0;
    }

    return sprintf($format, $bytes / pow($mod, $power), $units[$power]);
}
ryeguy
  • 65,519
  • 58
  • 198
  • 260
  • Their idea of having an option between 1024 and 1000 power is good. But this implementation is really weird. `$force_unit` and `$si` seem to do the same thing. You can also pass any string with an "i" in it to `$force_unit`, because they test for position. The decimal formatting is also overkill. – Gus Neves Dec 01 '16 at 00:43
8

Just my alternative, short and clean:

/**
 * @param int $bytes Number of bytes (eg. 25907)
 * @param int $precision [optional] Number of digits after the decimal point (eg. 1)
 * @return string Value converted with unit (eg. 25.3KB)
 */
function formatBytes($bytes, $precision = 2) {
    $unit = ["B", "KB", "MB", "GB"];
    $exp = floor(log($bytes, 1024)) | 0;
    return round($bytes / (pow(1024, $exp)), $precision).$unit[$exp];
}

or, more stupid and efficent:

function formatBytes($bytes, $precision = 2) {
    if ($bytes > pow(1024,3)) return round($bytes / pow(1024,3), $precision)."GB";
    else if ($bytes > pow(1024,2)) return round($bytes / pow(1024,2), $precision)."MB";
    else if ($bytes > 1024) return round($bytes / 1024, $precision)."KB";
    else return ($bytes)."B";
}
guari
  • 3,715
  • 3
  • 28
  • 25
8

use this function if you want a short code

bcdiv()

$size = 11485760;
echo bcdiv($size, 1048576, 0); // return: 10

echo bcdiv($size, 1048576, 2); // return: 10,9

echo bcdiv($size, 1048576, 2); // return: 10,95

echo bcdiv($size, 1048576, 3); // return: 10,953
wormhit
  • 3,687
  • 37
  • 46
Yanni
  • 2,583
  • 2
  • 20
  • 7
5

I know it's maybe a little late to answer this question but, more data is not going to kill someone. Here's a very fast function :

function format_filesize($B, $D=2){
    $S = 'BkMGTPEZY';
    $F = floor((strlen($B) - 1) / 3);
    return sprintf("%.{$D}f", $B/pow(1024, $F)).' '.@$S[$F].'B';
}

EDIT: I updated my post to include the fix proposed by camomileCase:

function format_filesize($B, $D=2){
    $S = 'kMGTPEZY';
    $F = floor((strlen($B) - 1) / 3);
    return sprintf("%.{$D}f", $B/pow(1024, $F)).' '.@$S[$F-1].'B';
}
David Bélanger
  • 7,400
  • 4
  • 37
  • 55
5

Simple function

function formatBytes($size, $precision = 0){
    $unit = ['Byte','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];

    for($i = 0; $size >= 1024 && $i < count($unit)-1; $i++){
        $size /= 1024;
    }

    return round($size, $precision).' '.$unit[$i];
}

echo formatBytes('1876144', 2);
//returns 1.79 MiB
SebHallin
  • 881
  • 6
  • 11
4

Extremely simple function to get human file size.

Original source: http://php.net/manual/de/function.filesize.php#106569

Copy/paste code:

<?php
function human_filesize($bytes, $decimals = 2) {
  $sz = 'BKMGTP';
  $factor = floor((strlen($bytes) - 1) / 3);
  return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
}
?>
John Erck
  • 9,478
  • 8
  • 61
  • 71
  • This is a nice one, which I used. Simple enough if you understand some math, which probably is a valid assumption for programmers. But: The factor should be 1000 to be correct, and this returns the quite uncommon PHP shorthand “42M”, not something one would use in UI like “42 MB“. – Andy Apr 23 '22 at 14:54
4
function convertToReadableSize($size)
{
  $base = log($size) / log(1024);
  $suffix = array("B", "KB", "MB", "GB", "TB");
  $f_base = floor($base);
  return round(pow(1024, $base - floor($base)), 1) . $suffix[$f_base];
}

Just call the function

echo convertToReadableSize(1024); // Outputs '1KB'
echo convertToReadableSize(1024 * 1024); // Outputs '1MB'
3

Flexible solution:

function size($size, array $options=null) {

    $o = [
        'binary' => false,
        'decimalPlaces' => 2,
        'decimalSeparator' => '.',
        'thausandsSeparator' => '',
        'maxThreshold' => false, // or thresholds key
        'suffix' => [
            'thresholds' => ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'],
            'decimal' => ' {threshold}B',
            'binary' => ' {threshold}iB',
            'bytes' => ' B'
        ]
    ];

    if ($options !== null)
        $o = array_replace_recursive($o, $options);

    $base = $o['binary'] ? 1024 : 1000;
    $exp = $size ? floor(log($size) / log($base)) : 0;

    if (($o['maxThreshold'] !== false) &&
        ($o['maxThreshold'] < $exp)
    )
        $exp = $o['maxThreshold'];

    return !$exp
        ? (round($size) . $o['suffix']['bytes'])
        : (
            number_format(
                $size / pow($base, $exp),
                $o['decimalPlaces'],
                $o['decimalSeparator'],
                $o['thausandsSeparator']
            ) .
            str_replace(
                '{threshold}',
                $o['suffix']['thresholds'][$exp],
                $o['suffix'][$o['binary'] ? 'binary' : 'decimal']
            )
        );
}

var_dump(size(disk_free_space('/')));
// string(8) "14.63 GB"
var_dump(size(disk_free_space('/'), ['binary' => true]));
// string(9) "13.63 GiB"
var_dump(size(disk_free_space('/'), ['maxThreshold' => 2]));
// string(11) "14631.90 MB"
var_dump(size(disk_free_space('/'), ['binary' => true, 'maxThreshold' => 2]));
// string(12) "13954.07 MiB"
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Pavel Tzonkov
  • 264
  • 3
  • 9
2

My approach

    function file_format_size($bytes, $decimals = 2) {
  $unit_list = array('B', 'KB', 'MB', 'GB', 'PB');

  if ($bytes == 0) {
    return $bytes . ' ' . $unit_list[0];
  }

  $unit_count = count($unit_list);
  for ($i = $unit_count - 1; $i >= 0; $i--) {
    $power = $i * 10;
    if (($bytes >> $power) >= 1)
      return round($bytes / (1 << $power), $decimals) . ' ' . $unit_list[$i];
  }
}
user24087
  • 29
  • 2
  • This answer is missing its educational explanation. I have concerns regarding `KB` and `KiB`. https://3v4l.org/s9AXo Please see https://cseducators.stackexchange.com/q/4425 – mickmackusa Apr 06 '21 at 07:15
  • This answer is missing its educational explanation. – mickmackusa Apr 06 '21 at 07:39
2

I don't know why you should make it so complicated as the others.

The following code is much simpler to understand and about 25% faster than the other solutions who uses the log function (called the function 20 Mio. times with different parameters)

function formatBytes($bytes, $precision = 2) {
    $units = ['Byte', 'Kilobyte', 'Megabyte', 'Gigabyte', 'Terabyte'];
    $i = 0;

    while($bytes > 1024) {
        $bytes /= 1024;
        $i++;
    }
    return round($bytes, $precision) . ' ' . $units[$i];
}
2

Here is an option using log10:

<?php

function format_number(float $d): string {
   $e = (int)(log10($d) / 3);
   return sprintf('%.3f', $d / 1e3 ** $e) . ['', ' k', ' M', ' G'][$e];
}

$s = format_number(9012345678);
var_dump($s == '9.012 G');

https://php.net/function.log10

Zombo
  • 1
  • 62
  • 391
  • 407
2

My own implementation for getting formatted file size from integer size. Simple to understand and easy to extend to accommodate larger files - Just follow the pattern.

<?php

function getFormattedFileSize($size, $precision) 
{
    switch (true) 
    {
        case ($size/1024 < 1):
            return $size.'B';
        case ($size/pow(1024, 2) < 1):
            return round($size/1024, $precision).'KB';
        case ($size/pow(1024, 3) < 1):
            return round($size/pow(1024, 2), $precision).'MB';
        case ($size/pow(1024, 4) < 1):
            return round($size/pow(1024, 3), $precision).'GB';
        case ($size/pow(1024, 5) < 1):
            return round($size/pow(1024, 4), $precision).'TB';
        default:
            return 'Error: invalid input or file is too large.';
    }
}
Udo E.
  • 2,665
  • 2
  • 21
  • 33
1

I succeeded with following function,

    function format_size($size) {
        $mod = 1024;
        $units = explode(' ','B KB MB GB TB PB');
        for ($i = 0; $size > $mod; $i++) {
            $size /= $mod;
        }
        return round($size, 2) . ' ' . $units[$i];
    }
Janith Chinthana
  • 3,792
  • 2
  • 27
  • 54
1

try this ;)

function bytesToSize($bytes) {
                $sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
                if ($bytes == 0) return 'n/a';
                $i = intval(floor(log($bytes) / log(1024)));
                if ($i == 0) return $bytes . ' ' . $sizes[$i]; 
                return round(($bytes / pow(1024, $i)),1,PHP_ROUND_HALF_UP). ' ' . $sizes[$i];
            }
echo bytesToSize(10000050300);
Yahia Mgarrech
  • 682
  • 9
  • 14
1
function changeType($size, $type, $end){
    $arr = ['B', 'KB', 'MB', 'GB', 'TB'];
    $tSayi = array_search($type, $arr);
    $eSayi = array_search($end, $arr);
    $pow = $eSayi - $tSayi;
    return $size * pow(1024 * $pow) . ' ' . $end;
}

echo changeType(500, 'B', 'KB');
Kerem Çakır
  • 401
  • 2
  • 7
  • 19
1

Albeit a bit stale, this library offers a tested and robust conversion API:

https://github.com/gabrielelana/byte-units

Once installed:

\ByteUnits\Binary::bytes(1024)->format();

// Output: "1.00KiB"

And to convert in the other direction:

\ByteUnits\Binary::parse('1KiB')->numberOfBytes();

// Output: "1024"

Beyond basic conversion, it offers methods for addition, subtraction, comparison, etc.

I am no way affiliated with this library.

Ben Johnson
  • 2,507
  • 3
  • 29
  • 29
1

I did this converting all input to byte and so converting to any output needed. Also, I used a auxiliar function to get base 1000 or 1024, but left it flex to decide use 1024 on popular type (without 'i', like MB instead of MiB).

    public function converte_binario($size=0,$format_in='B',$format_out='MB',$force_in_1024=false,$force_out_1024=false,$precisao=5,$return_format=true,$decimal=',',$centena=''){
    $out = false;

    if( (is_numeric($size)) && ($size>0)){
        $in_data = $this->converte_binario_aux($format_in,$force_in_1024);
        $out_data = $this->converte_binario_aux($format_out,$force_out_1024);

        // se formato de entrada e saída foram encontrados
        if( ((isset($in_data['sucesso'])) && ($in_data['sucesso']==true)) && ((isset($out_data['sucesso'])) && ($out_data['sucesso']==true))){
            // converte formato de entrada para bytes.
            $size_bytes_in = $size * (pow($in_data['base'], $in_data['pot']));
            $size_byte_out = (pow($out_data['base'], $out_data['pot']));
            // transforma bytes na unidade de destino
            $out = number_format($size_bytes_in / $size_byte_out,$precisao,$decimal,$centena);
            if($return_format){
                $out .= $format_out;
            }
        }
    }
    return $out;
}

public function converte_binario_aux($format=false,$force_1024=false){
    $out = [];
    $out['sucesso'] = false;
    $out['base'] = 0;
    $out['pot'] = 0;
    if((is_string($format) && (strlen($format)>0))){
        $format = trim(strtolower($format));
        $units_1000 = ['b','kb' ,'mb' ,'gb' ,'tb' ,'pb' ,'eb' ,'zb' ,'yb' ];
        $units_1024 = ['b','kib','mib','gib','tib','pib','eib','zib','yib'];
        $pot = array_search($format,$units_1000);
        if( (is_numeric($pot)) && ($pot>=0)){
            $out['pot'] = $pot;
            $out['base'] = 1000;
            $out['sucesso'] = true;
        }
        else{
            $pot = array_search($format,$units_1024);
            if( (is_numeric($pot)) && ($pot>=0)){
                $out['pot'] = $pot;
                $out['base'] = 1024;
                $out['sucesso'] = true;
            }
        }
        if($force_1024){
            $out['base'] = 1024;
        }
    }
    return $out;
}
0
function byte_format($size) {
    $bytes = array( ' KB', ' MB', ' GB', ' TB' );
    foreach ($bytes as $val) {
        if (1024 <= $size) {
            $size = $size / 1024;
            continue;
        }
        break;
    }
    return round( $size, 1 ) . $val;
}
Jesse
  • 6,725
  • 5
  • 40
  • 45
mooky
  • 11
  • 1
0

Here is simplified implementation of the Drupal format_size function:

/**
 * Generates a string representation for the given byte count.
 *
 * @param $size
 *   A size in bytes.
 *
 * @return
 *   A string representation of the size.
 */
function format_size($size) {
  if ($size < 1024) {
    return $size . ' B';
  }
  else {
    $size = $size / 1024;
    $units = ['KB', 'MB', 'GB', 'TB'];
    foreach ($units as $unit) {
      if (round($size, 2) >= 1024) {
        $size = $size / 1024;
      }
      else {
        break;
      }
    }
    return round($size, 2) . ' ' . $unit;
  }
}
ya.teck
  • 2,060
  • 28
  • 34
0

Base on Leo's answer, add

  • Support for negative
  • Support 0 < value < 1 ( Ex: 0.2, will cause log(value) = negative number )

If you want max unit to Mega, change to $units = explode(' ', ' K M');


function formatUnit($value, $precision = 2) {
    $units = explode(' ', ' K M G T P E Z Y');

    if ($value < 0) {
        return '-' . formatUnit(abs($value));
    }

    if ($value < 1) {
        return $value . $units[0];
    }

    $power = min(
        floor(log($value, 1024)),
        count($units) - 1
    );

    return round($value / pow(1024, $power), $precision) . $units[$power];
}
Steely Wing
  • 16,239
  • 8
  • 58
  • 54
-1

It's a little late but a slightly faster version of the accepted answer is below:

function formatBytes($bytes, $precision)
{
    $unit_list = array
    (
        'B',
        'KB',
        'MB',
        'GB',
        'TB',
    );

    $bytes = max($bytes, 0);
    $index = floor(log($bytes, 2) / 10);
    $index = min($index, count($unit_list) - 1);
    $bytes /= pow(1024, $index);

    return round($bytes, $precision) . ' ' . $unit_list[$index];
}

It's more efficient, due to performing a single log-2 operation instead of two log-e operations.

It's actually faster to do the more obvious solution below, however:

function formatBytes($bytes, $precision)
{
    $unit_list = array
    (
        'B',
        'KB',
        'MB',
        'GB',
        'TB',
    );

    $index_max = count($unit_list) - 1;
    $bytes = max($bytes, 0);

    for ($index = 0; $bytes >= 1024 && $index < $index_max; $index++)
    {
        $bytes /= 1024;
    }

    return round($bytes, $precision) . ' ' . $unit_list[$index];
}

This is because as the index is calculated at the same time as the value of the number of bytes in the appropriate unit. This cut the execution time by about 35% (a 55% speed increase).

-1

Another condensed implementation which can translate to the base 1024 (binary) or base 1000 (decimal) and also works with incredibly large numbers hence of the use of the bc library:

function renderSize($byte,$precision=2,$mibi=true)
{
    $base = (string)($mibi?1024:1000);
    $labels = array('K','M','G','T','P','E','Z','Y');
    for($i=8;$i>=1;$i--)
        if(bccomp($byte,bcpow($base, $i))>=0)
            return bcdiv($byte,bcpow($base, $i), $precision).' '.$labels[$i-1].($mibi?'iB':'B');
    return $byte.' Byte';
}
Christian
  • 582
  • 9
  • 21
-1

I figured I would add a meshing of two submitters code (Using John Himmelman's code, which is in this thread, and using Eugene Kuzmenko's code) that I'm using.

function swissConverter($value, $format = true, $precision = 2) {
    //Below converts value into bytes depending on input (specify mb, for 
    //example)
    $bytes = preg_replace_callback('/^\s*(\d+)\s*(?:([kmgt]?)b?)?\s*$/i', 
    function ($m) {
        switch (strtolower($m[2])) {
          case 't': $m[1] *= 1024;
          case 'g': $m[1] *= 1024;
          case 'm': $m[1] *= 1024;
          case 'k': $m[1] *= 1024;
        }
        return $m[1];
        }, $value);
    if(is_numeric($bytes)) {
        if($format === true) {
            //Below converts bytes into proper formatting (human readable 
            //basically)
            $base = log($bytes, 1024);
            $suffixes = array('', 'KB', 'MB', 'GB', 'TB');   

            return round(pow(1024, $base - floor($base)), $precision) .' '. 
                     $suffixes[floor($base)];
        } else {
            return $bytes;
        }
    } else {
        return NULL; //Change to prefered response
    }
}

This uses Eugene's code to format the $value into bytes (I keep my data in MB, so it converts my data: 10485760 MB into 10995116277760) - it then uses John's code to convert it into the proper display value (10995116277760 into 10 TB).

I've found this really helpful - so my thanks to the two submitters!

EML
  • 1
  • 4
-1

I developed my own function that convert human readable memory size to different sizes.

function convertMemorySize($strval, string $to_unit = 'b')
{
    $strval    = strtolower(str_replace(' ', '', $strval));
    $val       = floatval($strval);
    $to_unit   = strtolower(trim($to_unit))[0];
    $from_unit = str_replace($val, '', $strval);
    $from_unit = empty($from_unit) ? 'b' : trim($from_unit)[0];
    $units     = 'kmgtph';  // (k)ilobyte, (m)egabyte, (g)igabyte and so on...


    // Convert to bytes
    if ($from_unit !== 'b')
        $val *= 1024 ** (strpos($units, $from_unit) + 1);


    // Convert to unit
    if ($to_unit !== 'b')
        $val /= 1024 ** (strpos($units, $to_unit) + 1);


    return $val;
}


convertMemorySize('1024Kb', 'Mb');  // 1
convertMemorySize('1024', 'k')      // 1
convertMemorySize('5.2Mb', 'b')     // 5452595.2
convertMemorySize('10 kilobytes', 'bytes') // 10240
convertMemorySize(2048, 'k')        // By default convert from bytes, result is 2

This function accepts any memory size abbreviation like "Megabyte, MB, Mb, mb, m, kilobyte, K, KB, b, Terabyte, T...." so it is typo safe.

Juan Lago
  • 960
  • 1
  • 10
  • 20