<?php

function formatFloat($value)
{
    $phpPrecision = 14;

    if ($value == 0.0)  return '0.0';
    
    if (log10(abs($value)) < $phpPrecision) {
    
        $decimalDigits = max(
            ($phpPrecision - 1) - floor(log10(abs($value))),
            0
        );

        $formatted = number_format($value, $decimalDigits);

        // Trim excess 0's
        $formatted = preg_replace('/(\.[0-9]+?)0*$/', '$1', $formatted);

        return $formatted;

    }
    
    $formattedWithoutCommas = number_format($value, 0, '.', '');
    
    $sign = (strpos($formattedWithoutCommas, '-') === 0) ? '-' : '';

    // Extract the unsigned integer part of the number
    preg_match('/^-?(\d+)(\.\d+)?$/', $formattedWithoutCommas, $components);
    $integerPart = $components[1];

    // Split into significant and insignificant digits
    $significantDigits   = substr($integerPart, 0, $phpPrecision);
    $insignificantDigits = substr($integerPart, $phpPrecision);

    // Round the significant digits (using the insignificant digits)
    $fractionForRounding = (float) ('0.' . $insignificantDigits);
    $rounding            = (int) round($fractionForRounding);  // Either 0 or 1
    $rounded             = $significantDigits + $rounding;
    
    // Pad on the right with zeros
    $formattingString = '%0-' . strlen($integerPart) . 's';
    $formatted        = sprintf($formattingString, $rounded);
    
    // Insert a comma between every group of thousands
    $formattedWithCommas = strrev(
        rtrim(
            chunk_split(
                strrev($formatted), 3, ','
            ),
            ','
        )
    );

    return $sign . $formattedWithCommas;
}

$randomFloats = array();

for ($i = 0; $i < 50; $i++) {
    $float  = mt_rand() / mt_getrandmax();
    $float  = round($float, mt_rand(0, 15));
    $float *= pow(10, mt_rand(-25, 25));
    if (mt_rand() / mt_getrandmax() > 0.5) {
        $float *= -1.0;
    }
    $randomFloats[] = $float;
}

sort($randomFloats);

function formatFloat2(
        $value,
        $noOfDigits = 14,
        $separator = ',',
        $decimal = '.'
    ) {

    $exponent = floor(log10(abs($value)));
    $magnitude = pow(10, $exponent);
    
    $mantissa = (string)abs(round(($value /  pow(10, $exponent - $noOfDigits + 1))));
    $formattedNum = '';

    if ($exponent >= 0) { // <=> if ($value >= 1)
        
        // just for pre-formatting
        $formattedNum = number_format($value, $noOfDigits - 1, $decimal, $separator);

        // then report digits from $mantissa into $formattedNum
        $formattedLen = strlen($formattedNum);
        $mantissaLen = strlen($mantissa);
        for ($fnPos = 0, $mPos = 0; $fnPos <  $formattedLen; $fnPos++, $mPos++) {

            // skip non-digit
            while($formattedNum[$fnPos] === '-' || $formattedNum[$fnPos] === $separator || $formattedNum[$fnPos] === $decimal) {
                $fnPos++;
            }
            $formattedNum[$fnPos] = $mPos < $mantissaLen ? $mantissa[$mPos] : '0';
            
        }
        
    } else { // <=> if ($value < 1)
        
        // prepend minus sign if necessary
        if ($value < 0) {
            $formattedNum = '-';
        }
        $formattedNum .= '0' . $decimal . str_repeat('0', abs($exponent) - 1) . $mantissa;
        
    }
    
    
    // strip trailing decimal zeroes
    $formattedNum = preg_replace('/\.?0*$/', '', $formattedNum);
    
    return $formattedNum;
    
}

foreach ($randomFloats as $float) {
    echo sprintf('%20s', $float) . "\n --> " . formatFloat($float) . "\n --> " . formatFloat2($float) . "\n";

}
