Задача: У нас есть большое число с кучей нулей, или может просто большое число, которое неудобно читать. Нам нужно преобразовать его в удобное для чтение число. Например, число 1500
, должно превратиться в 1,5 тыс.
.
В WP есть подобная функция, только для преобразования байтов в килобайты, мегабайты. См. size_format().
Решение: конвертация больших чисел в текстовый вид
Код ниже преобразовывает переданное число в читаемую форму 1 тысяча, 1 миллион, 1 миллиард.
Вариант 1 (рекурсия)
/**
* Convert big number to readable format.
*
* @param int|string $num Number to format.
* @param int $decimals Optional. Precision of number of decimal places. Default: 0.
* @param string $return_type Optional. Return format. One of: `string`, `object`. Default: string.
* @param int $_depth Internal. For recursion.
*
* @return string|object Object with two elements: `num` and `unit`.
*
* @version 1.3
*/
function number_to_human( $number, int $decimals = 1, string $return_type = 'string', int $_depth = 0 ) {
static $abbrs;
$abbrs || $abbrs = [ '',
__( 'тыс.', 'hl' ),
__( 'млн.', 'hl' ),
__( 'млрд.', 'hl' ),
__( 'трлн.', 'hl' ),
__( 'квдрлн.', 'hl' )
];
if( $number >= 1000 ){
return call_user_func( __FUNCTION__, $number / 1000, $decimals, $return_type, ++$_depth );
}
$number = number_format_i18n( $number, $decimals );
$float_sign = $GLOBALS['wp_locale']->number_format['decimal_point'];
if( strpos( $number, $float_sign ) ){
$number = rtrim( $number, '0' );
$number = rtrim( $number, $float_sign );
}
$abbr = $_depth ? $abbrs[ $_depth ] : '';
if( 'string' === $return_type ){
return trim( "$number $abbr" );
}
return (object) [
'num' => $number,
'abbr' => $abbr,
];
}
$arr = [
number_to_human( 0 ), // 0
number_to_human( 120 ), // 120
number_to_human( 16 ), // 16
number_to_human( 1654, 2 ), // 1,65 тыс.
number_to_human( 16504.234 ), // 16,5 тыс.
number_to_human( 16504.234, 2 ), // 16,5 тыс.
number_to_human( 254854564, 2 ), // 254,85 млн.
];
/*
Array (
[0] => 0
[1] => 120
[2] => 16
[3] => 1,65 тыс.
[4] => 16,5 тыс.
[5] => 16,5 тыс.
[6] => 254,85 млн.
)
*/
<?php $data = number_to_human( 254854564, 2, 'object' ) ?>
<div class="price">
<span class="number"><?= $data->num; // 254,9 ?></span>
<span class="abbr"><?= $data->abbr; // млн. ?></span>
</div>
Вариант 2 (без рекурсии)
/**
* Convert big number to readable format.
*
* @param int|float|string $num Number to format.
* @param int $decimals Optional. Precision of number of decimal places. Default: 0.
*
* @return object Object with two elements: `num` and `unit`.
*
* @version 1.1
*/
function num_to_human( $num, int $decimals = 1 ) {
static $units;
$units || $units = [
'квдрлн.' => 1000 ** 5,
'трлн.' => 1000 ** 4,
'млрд.' => 1000 ** 3,
'млн.' => 1000 ** 2,
'тыс.' => 1000,
'' => 1,
];
$number = 0;
foreach( $units as $unit => $mag ){
if ( (float) $num >= $mag ) {
$number = $num / $mag;
break;
}
}
$number = number_format_i18n( $number, $decimals );
if( preg_match( '/[,.]/', $number, $match ) ){
$number = rtrim( $number, '0' );
$number = rtrim( $number, $match[0] );
}
return (object) [
'num' => $number,
'unit' => $unit
];
}
Пример использования:
$arr = [
num_to_human( 0 ), // 0
num_to_human( 120 ), // 120
num_to_human( 16 ), // 16
num_to_human( 1654, 2 ), // 1,65 тыс.
num_to_human( 16504.234 ), // 16,5 тыс.
num_to_human( 16504.234, 2 ), // 16,5 тыс.
num_to_human( 254854564, 2 ), // 254,85 млн.
];
foreach( $arr as $res ){
echo "$res->num $res->unit\n";
}
/*
0
120
16
1,65 тыс.
16,5 тыс.
16,5 тыс.
254,85 млн.
*/
Класс Объединяющий в себе несколько функций
GitHub
<?php
namespace Kama\WP;
interface Num_Format_Interface {
public function human_k( $number, $decimals = 1 ): string;
public function human_short( $number, $decimals = 1 ): string;
public function human_abbr( $number, $decimals = 1 ): string;
public function smart( $number, int $show_decimals = 2 ): string;
public function flex( $number, int $decimals = 2 ): string;
public function fixed( $number, int $decimals = 2 ): string;
}
/**
*
* @see number_format_i18n()
*
* @version 3.2
*/
class Num_Format implements Num_Format_Interface {
/**
* Format number. Thousands become k: 23 000 > 23k.
*
* @param float|string $number
* @param int|string $decimals Optional. Precision of number of decimal places. Default 0.
* Specify string as 'decimal float_round_type': '3 flex', '2 smart', '3 fixed'
* to set float round function. {@see _human_unit}.
*
* @return string
*/
public function human_k( $number, $decimals = 1 ): string {
static $names;
$names || $names = [ '', 'k', 'kk', 'kkk', 'kkkk', 'kkkkk' ];
return $this->_human_unit( $number, $decimals, $names, '%s' );
}
/**
* Format number. Example: 23 000 000 > 23M.
*
* @param float|string $number
* @param int|string $decimals Optional. Precision of number of decimal places. Default 0.
* Specify string as 'decimal float_round_type': '3 flex', '2 smart', '3 fixed'
* to set float round function. {@see _human_unit}.
*
* @return string
*/
public function human_short( $number, $decimals = 1 ): string {
static $names;
$names || $names = [ '', 'K', 'M', 'B', 'T', 'Q' ];
return $this->_human_unit( $number, $decimals, $names, '%s' );
}
/**
* Convert big number to readable format.
*
* @param int|string $num Original Number.
* @param int|string $decimals Optional. Precision of number of decimal places. Default 0.
* Specify string as 'decimal float_round_type': '3 flex', '2 smart', '3 fixed'
* to set float round function. {@see _human_unit}.
*
* @return string
*/
public function human_abbr( $number, $decimals = 1 ): string {
static $names;
$names || $names = [ '',
__( 'тыс.', 'hl' ),
__( 'млн.', 'hl' ),
__( 'млрд.', 'hl' ),
__( 'трлн.', 'hl' ),
__( 'квдрлн.', 'hl' ),
];
return $this->_human_unit( $number, $decimals, $names, ' %s' );
}
/**
*
* @param float|string $number Original Number.
* @param int|string $decimals Optional. Precision of number of decimal places. Default 0.
* Specify string as 'decimal float_round_type': '3 flex', '2 smart', '3 fixed'
* to set float round function.
* @param array $names
* @param string $unit_patt Pattern to display units.
*
* @return string
*/
private function _human_unit( $number, $decimals, array $names, string $unit_patt = '' ): string {
[ $number, $depth ] = self::_human_unit_depth( $number );
if( ! $number ){
return '0';
}
[ $decimals, $floats_round_type ] = explode( ' ', $decimals ) + [ 2, '' ];
$unit_suffix = $depth ? sprintf( $unit_patt, $names[ $depth ] ) : '';
switch( $floats_round_type ){
case 'fixed':
return $this->fixed( $number, $decimals ) . $unit_suffix;
case 'smart':
return $this->smart( $number, $decimals ) . $unit_suffix;
default:
return $this->flex( $number, $decimals ) . $unit_suffix;
}
}
/**
* Convert big number to readable format.
* Supports negative numbers.
* Can be used as root function. I.e. wrap it to your
* own function where pass $names and specify desired output.
*
* @param float|string $number Original Number.
* @param int $depth Internal.
*
* @return array
*/
private static function _human_unit_depth( $number, int $depth = 0 ): array {
$sign = $number <=> 0;
$number = abs( $number );
if( $number >= 1000 ){
return self::_human_unit_depth( $number * $sign / 1000, ++$depth );
}
return [ $number * $sign, $depth ];
}
/**
* Format number. Smart calculate zeros after dot and
* leave specified $show_decimals numbers of digits.
*
* Example:
* - 0.0000000123 > 0.000000012;
* - 0.0123 > 0.012;
* - 2.999951132432 > 2.999951;
*
* @param float|string $number
* @param int|null $show_decimals
*
* @return string
*/
public function smart( $number, int $show_decimals = 2 ): string {
if( ! $number ){
return '';
}
$decimals = $show_decimals;
// use simple formats fo big numbers
if( $number < 30 && $number > -30 ){
$number = (float) $number;
// increase $decimals for numbers like: n.000nnn | n.999nnn
preg_match( "/\d+\.((?:0{2,}|9{2,})\d{1,$show_decimals})/", sprintf( '%.12f', $number ), $mm );
if( $mm ){
$decimals = strlen( $mm[1] );
}
}
return $this->flex( $number, $decimals );
}
/**
* Format number and trim ending zeros.
*
* @param float|string $number
* @param int $decimals Optional. Precision of number of decimal places. Default 0.
*
* @return string
* @see number_format_i18n()
*/
public function flex( $number, int $decimals = 2 ): string {
if( ! $number ){
return '';
}
$number = $this->fixed( $number, $decimals );
// 38 020.00 > 38 020
// 38 020.00100 > 38 020.001
// 38 020 > 38 020
$float_sign = $GLOBALS['wp_locale']->number_format['decimal_point'];
if( strpos( $number, $float_sign ) ){
$number = rtrim( $number, '0' );
$number = rtrim( $number, $float_sign );
}
return $number;
}
/**
* Format number and always leave specified ending decimals.
*
* @param float|string $number
* @param int $decimals Optional. Precision of number of decimal places. Default 0.
*
* @return string
* @see number_format_i18n()
*/
public function fixed( $number, int $decimals = 2 ): string {
if( ! $number ){
return '';
}
return number_format_i18n( $number, $decimals );
}
}
Пример использования:
GitHub
<?php
$format = new \Kama\WP\Num_Format();
$test = [
$format->human_abbr( 0 ), // 0
$format->human_abbr( 1.0654 ), // 1,1
$format->human_abbr( 16 ), // 16
$format->human_abbr( 1654 ), // 1,7 тыс.
$format->human_abbr( 16504.234 ), // 16,5 тыс.
$format->human_abbr( 16504.0000234 ), // 16,5 тыс.
$format->human_abbr( 254854564 ), // 254,9 млн.
'',
$format->human_k( 16565404.0000234 ), // 16,6kk
$format->human_k( 254856544564 ), // 254,9kkk
'',
$format->human_k( 2.00000231, '2 smart' ), // 2,0000023
$format->human_k( 2.00000231, '2 fixed' ), // 2,00
$format->human_k( 2.00000231, '2 flex' ), // 2
$format->human_k( 2.231, '3 smart' ), // 2,23
$format->human_k( 2.231, '3 fixed' ), // 2,23
$format->human_k( 2.231, '3 flex' ), // 2,23
'',
$format->human_short( 16504.0000234 ), // 16,5K
$format->human_short( 254854564 ), // 254,9M
'',
$format->flex( 16504.0000234 ), // 16 504
$format->flex( 16504.0100 ), // 16 504,01
$format->flex( 254854564 ), // 254 854 564
'',
$format->smart( 0.000111, 2 ), // 0,00011
$format->smart( 1.000111, 2 ), // 1,00011
$format->smart( 254854564 ), // 254 854 564
'',
$format->fixed( 0.000111, 3 ), // 0,000
$format->fixed( 1.0111, 3 ), // 1,011
$format->fixed( 254 ), // 254,00
];
print_r( $test );
--
П.С. Не редко слышу как числа называют цифрами. Например, "У него на счете большая цифра", "Цифра с шестью нулями". Не делайте так!