diff --git a/wp-includes/load.php b/wp-includes/load.php
index ae5ada388d..1a44906c11 100644
--- a/wp-includes/load.php
+++ b/wp-includes/load.php
@@ -1425,33 +1425,181 @@ function is_ssl() {
 }
 
 /**
- * Converts a shorthand byte value to an integer byte value.
+ * Returns byte size represented in a numeric php.ini directive.
+ *
+ * php.ini directives may use a string representation of a number of bytes
+ * or a "shorthand" byte size to reference larger values. Multiple numeric
+ * php.ini directive use these shorthands even when they don't refer bytes.
+ *
+ * Example:
+ *
+ *     wp_convert_hr_to_bytes( "1m" ) == 1048576
+ *     wp_convert_hr_to_bytes( "2K" ) == 2048 // 2 * 1024
+ *     wp_convert_hr_to_bytes( "0.5g" ) == 0
+ *     wp_convert_hr_to_bytes( "14.6e-13g" ) == 15032385536 // 14 * 1024^3
+ *     wp_convert_hr_to_bytes( "-813k" ) == 0;
+ *     wp_convert_hr_to_bytes( "boat" ) == 0;
+ *
+ *     // This gives an answer, but it's _wrong_ because
+ *     // the underlying mechanism in PHP overflowed and
+ *     // the real return value depends on whether PHP
+ *     // was built with 64-bit support.
+ *     wp_convert_hr_to_bytes( "9223372036854775807g" ) == ??
+ *
+ * Notes:
+ *  - Suffix units are case-insensitive and are always determined
+ *    by looking at the last character in the input string.
+ *  - Suffix units k/m/g report powers of 1024. PHP and the IEC disagree
+ *    on the meaning of "kilobyte," "megabyte," and "gigabyte."
+ *  - This function will not fail; it stops parsing after finding
+ *    the last consecutive digit at the front of the trimmed string.
+ *  - Invalid string representations return a value of 0.
+ *  - This code mirrors the computation inside PHP and should only
+ *    change its output if PHP redefines how it parses numeric php.ini
+ *    directive. If you think something is wrong or should be
+ *    refactored it probably shouldn't be. Consult `zend_operators.c`
+ *    in the PHP-SRC repository for more info; specifically
+ *    `zend_atol()` and `_is_numeric_string_ex()`.
+ *  - As noted in the PHP documentation, any numeric value that overflows
+ *    an integer for the platform on which PHP is built will break.
  *
  * @since 2.3.0
  * @since 4.6.0 Moved from media.php to load.php.
+ * @since 6.1.0 Mirrors size calculation in PHP.
  *
  * @link https://www.php.net/manual/en/function.ini-get.php
  * @link https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
+ * @link https://en.wikipedia.org/wiki/Byte#Multiple-byte_units
  *
- * @param string $value A (PHP ini) byte value, either shorthand or ordinary.
- * @return int An integer byte value.
+ * @param string $value A numeric php.ini directive's byte value,
+ *                      either shorthand or ordinary, as returned
+ *                      by a call to `ini_get()`.
+ * @return int          Parsed numeric value represented by given string.
  */
 function wp_convert_hr_to_bytes( $value ) {
-	$value = strtolower( trim( $value ) );
-	$bytes = (int) $value;
+	/** @var int Number of bytes in input string; we're only assessing 7-bit ASCII/Unicode characters. */
+	$strlen = strlen( $value );
+	/** @var int|float Count (of bytes) represented by value string. */
+	$scalar = 0;
+	/** @var int Sign of number represented by input, either positive (1) or negative (-1). */
+	$sign = 1;
+	/** @var int Numeric base of digits; determined by string prefix (e.g. "0x" or "0"). */
+	$base = 10;
+	/** @var int Index into input string as we walk through it and analyze each character. */
+	$i = 0;
+
+	// Trim leading whitespace.
+	for ( ; $i < $strlen; $i++ ) {
+		switch ( $value[ $i ] ) {
+			case ' ':
+			case '\t':
+			case '\r':
+			case '\v':
+			case '\f':
+				break;
+
+			default:
+				break 2;
+		}
+	}
+
+	// Handle optional sign indicator.
+	switch ( $value[ $i ] ) {
+		case '+':
+			$i++;
+			break;
+
+		case '-':
+			$sign = -1;
+			$i++;
+			break;
+	}
 
-	if ( false !== strpos( $value, 'g' ) ) {
-		$bytes *= GB_IN_BYTES;
-	} elseif ( false !== strpos( $value, 'm' ) ) {
-		$bytes *= MB_IN_BYTES;
-	} elseif ( false !== strpos( $value, 'k' ) ) {
-		$bytes *= KB_IN_BYTES;
+	// Determine base for digit conversion, if not decimal.
+	$base_a = $value[ $i ];
+	$base_b = $i + 1 < $strlen ? $value[ $i + 1 ] : '';
+
+	if ( $base_a === '0' && ( $base_b === 'x' || $base_b === 'X' ) ) {
+		$base = 16;
+		$i += 2;
+	} else if ( $base_a === '0' && ctype_digit( $base_b ) ) {
+		$base = 8;
+		$i += 1;
+	}
+
+	// Trim leading zeros.
+	for ( ; $i < $strlen; $i++ ) {
+		if ( '0' !== $value[ $i ] ) {
+			break;
+		}
 	}
 
-	// Deal with large (float) values which run into the maximum integer size.
-	return min( $bytes, PHP_INT_MAX );
+	/** @var array Numeric values for scanned digits. */
+	$digits = [
+		'0' =>  0, '1' =>  1, '2' =>  2, '3' =>  3, '4' =>  4,
+		'5' =>  5, '6' =>  6, '7' =>  7, '8' =>  8, '9' =>  9,
+		'A' => 10, 'a' => 10, 'B' => 11, 'b' => 11, 'C' => 12,
+		'c' => 12, 'D' => 13, 'd' => 13, 'E' => 14, 'e' => 14,
+		'F' => 15, 'f' => 15,
+	];
+
+	// Build the scalar value by eating the
+	// next sequence of contiguous digits.
+	for ( ; $i < $strlen; $i++) {
+		$c = $value[ $i ];
+
+		// Only digits recognized in this base system can be used.
+		// Once we find an unrecognized digit we abort and move
+		// on to the next step in parsing the size suffix.
+		if ( ! isset( $digits[ $c ] ) || $digits[ $c ] >= $base ) {
+			break;
+		}
+
+		$scalar = $scalar * $base + $digits[ $c ];
+	}
+
+	// Clamp the parsed digits to an integer value as PHP does internally.
+	// See `strtol()`/`LONG_MAX` for 32-bit systems and `strtoll()`/`LLONG_MAX` for 64-bit.
+	if ( $sign > 0 && $scalar >= PHP_INT_MAX ) {
+		$scalar = PHP_INT_MAX;
+	} else if ( $sign < 0 && $scalar >= -PHP_INT_MIN ) {
+		$scalar = PHP_INT_MIN;
+	} else if ( $sign < 0 ) {
+		$scalar = -$scalar;
+	}
+
+	// Do not use WP constants here (GB_IN_BYTES, MB_IN_BYTES, KB_IN_BYTES)
+	// since they are re-definable; PHP shorthand values are hard-coded
+	// in PHP itself and stay the same regardless of these constants.
+	//
+	// Note that we can overflow here, as happens in PHP itself.
+	// Overflow results will likely not match PHP's value, but
+	// will likely break in most cases anyway and so leaving
+	// this loose is the best we can do until and unless PHP
+	// makes a more concrete choice on how to handle overflow.
+	//
+	// Fallthrough preserved to match structure and behavior in PHP.
+	switch ( $value[ $strlen - 1 ] ) {
+		case 'g':
+		case 'G':
+			$scalar *= 1024;
+			// Fallthrough
+
+		case 'm':
+		case 'M':
+			$scalar *= 1024;
+			// Fallthrough
+
+		case 'k':
+		case 'K':
+			$scalar *= 1024;
+			// Fallthrough
+	}
+
+	return (int) $scalar;
 }
 
+
 /**
  * Determines whether a PHP ini value is changeable at runtime.
  *
