framework/i18n/Formatter.php
protected function asDecimalStringFallback($value, $decimals = 2)
    {
        if (empty($value)) {
            $value = 0;
        }

        $value = $this->normalizeNumericStringValue((string) $value);

        $separatorPosition = strrpos($value, '.');

        if ($separatorPosition !== false) {
            $integerPart = substr($value, 0, $separatorPosition);
            $fractionalPart = substr($value, $separatorPosition + 1);
        } else {
            $integerPart = $value;
            $fractionalPart = null;
        }

        $decimalOutput = '';

        if ($decimals === null) {
            $decimals = 2;
        }

        $carry = 0;

        if ($decimals > 0) {
            $decimalSeparator = $this->decimalSeparator;
            if ($this->decimalSeparator === null) {
                $decimalSeparator = '.';
            }

            if ($fractionalPart === null) {
                $fractionalPart = str_repeat('0', $decimals);
            } elseif (strlen($fractionalPart) > $decimals) {
                $cursor = $decimals;

                // checking if fractional part must be rounded
                if ((int) substr($fractionalPart, $cursor, 1) >= 5) {
                    while (--$cursor >= 0) {
                        $carry = 0;

                        $oneUp = (int) substr($fractionalPart, $cursor, 1) + 1;
                        if ($oneUp === 10) {
                            $oneUp = 0;
                            $carry = 1;
                        }

                        $fractionalPart = substr($fractionalPart, 0, $cursor) . $oneUp . substr($fractionalPart, $cursor + 1);

                        if ($carry === 0) {
                            break;
                        }
                    }
                }

                $fractionalPart = substr($fractionalPart, 0, $decimals);
            } elseif (strlen($fractionalPart) < $decimals) {
                $fractionalPart = str_pad($fractionalPart, $decimals, '0');
            }

            $decimalOutput .= $decimalSeparator . $fractionalPart;
        }

        // checking if integer part must be rounded
        if ($carry || ($decimals === 0 && $fractionalPart !== null && (int) substr($fractionalPart, 0, 1) >= 5)) {
            $integerPartLength = strlen($integerPart);
            $cursor = 0;

            while (++$cursor <= $integerPartLength) {
                $carry = 0;

                $oneUp = (int) substr($integerPart, -$cursor, 1) + 1;
                if ($oneUp === 10) {
                    $oneUp = 0;
                    $carry = 1;
                }

                $integerPart = substr($integerPart, 0, -$cursor) . $oneUp . substr($integerPart, $integerPartLength - $cursor + 1);

                if ($carry === 0) {
                    break;
                }
            }
            if ($carry === 1) {
                $integerPart = '1' . $integerPart;
            }
        }

        if (strlen($integerPart) > 3) {
            $thousandSeparator = $this->thousandSeparator;
            if ($thousandSeparator === null) {
                $thousandSeparator = ',';
            }

            $integerPart = strrev(implode(',', str_split(strrev($integerPart), 3)));
            if ($thousandSeparator !== ',') {
                $integerPart = str_replace(',', $thousandSeparator, $integerPart);
            }
        }

        return $integerPart . $decimalOutput;
    }
protected function asDecimalStringFallback($value, $decimals = 2)
    {
        if (empty($value)) {
            $value = 0;
        }

        $value = $this->normalizeNumericStringValue((string) $value);

        $separatorPosition = strrpos($value, '.');

        if ($separatorPosition !== false) {
            $integerPart = substr($value, 0, $separatorPosition);
            $fractionalPart = substr($value, $separatorPosition + 1);
        } else {
            $integerPart = $value;
            $fractionalPart = null;
        }

        $decimalOutput = '';

        if ($decimals === null) {
            $decimals = 2;
        }

        $carry = 0;

        if ($decimals > 0) {
            $decimalSeparator = $this->decimalSeparator;
            if ($this->decimalSeparator === null) {
                $decimalSeparator = '.';
            }

            if ($fractionalPart === null) {
                $fractionalPart = str_repeat('0', $decimals);
            } elseif (strlen($fractionalPart) > $decimals) {
                $cursor = $decimals;

                // checking if fractional part must be rounded
                if ((int) substr($fractionalPart, $cursor, 1) >= 5) {
                    while (--$cursor >= 0) {
                        $carry = 0;

                        $oneUp = (int) substr($fractionalPart, $cursor, 1) + 1;
                        if ($oneUp === 10) {
                            $oneUp = 0;
                            $carry = 1;
                        }

                        $fractionalPart = substr($fractionalPart, 0, $cursor) . $oneUp . substr($fractionalPart, $cursor + 1);

                        if ($carry === 0) {
                            break;
                        }
                    }
                }

                $fractionalPart = substr($fractionalPart, 0, $decimals);
            } elseif (strlen($fractionalPart) < $decimals) {
                $fractionalPart = str_pad($fractionalPart, $decimals, '0');
            }

            $decimalOutput .= $decimalSeparator . $fractionalPart;
        }

        // checking if integer part must be rounded
        if ($carry || ($decimals === 0 && $fractionalPart !== null && (int) substr($fractionalPart, 0, 1) >= 5)) {
            $integerPartLength = strlen($integerPart);
            $cursor = 0;

            while (++$cursor <= $integerPartLength) {
                $carry = 0;

                $oneUp = (int) substr($integerPart, -$cursor, 1) + 1;
                if ($oneUp === 10) {
                    $oneUp = 0;
                    $carry = 1;
                }

                $integerPart = substr($integerPart, 0, -$cursor) . $oneUp . substr($integerPart, $integerPartLength - $cursor + 1);

                if ($carry === 0) {
                    break;
                }
            }
            if ($carry === 1) {
                $integerPart = '1' . $integerPart;
            }
        }

        if (strlen($integerPart) > 3) {
            $thousandSeparator = $this->thousandSeparator;
            if ($thousandSeparator === null) {
                $thousandSeparator = ',';
            }

            $integerPart = strrev(implode(',', str_split(strrev($integerPart), 3)));
            if ($thousandSeparator !== ',') {
                $integerPart = str_replace(',', $thousandSeparator, $integerPart);
            }
        }

        return $integerPart . $decimalOutput;
    }
Fallback for formatting value as a decimal number.

Property [[decimalSeparator]] will be used to represent the decimal point. The value is rounded automatically
to the defined decimal digits.

@param string|int|float $value the value to be formatted.
@param int $decimals the number of digits after the decimal point. The default value is `2`.
@return string the formatted result.
@see decimalSeparator
@see thousandSeparator
@since 2.0.16