1616use Override ;
1717
1818use function assert ;
19+ use function chr ;
1920use function in_array ;
2021use function ini_set ;
2122use function intdiv ;
@@ -167,7 +168,7 @@ public static function ten(): BigDecimal
167168 * Note that BigDecimal has no concept of negative zero, so `-0.0` and `0.0` both convert to zero.
168169 *
169170 * @throws InvalidArgumentException If the value is NaN or infinite.
170- * @throws UnsupportedPlatformException If PHP is not 64-bit, or if the platform uses a non-IEEE-754 double format.
171+ * @throws UnsupportedPlatformException If the platform uses a non-IEEE-754 double format.
171172 *
172173 * @pure
173174 */
@@ -180,36 +181,64 @@ public static function fromFloatExact(float $value): BigDecimal
180181 throw InvalidArgumentException::cannotConvertFloat ($ value > 0 ? 'INF ' : '-INF ' );
181182 }
182183
183- if (PHP_INT_SIZE < 8 ) {
184- throw UnsupportedPlatformException::require64BitPhp ();
185- }
186-
187184 if (pack ('E ' , 1.0 ) !== "\x3f\xf0\x00\x00\x00\x00\x00\x00" ) {
188185 throw UnsupportedPlatformException::unsupportedFloatFormat ();
189186 }
190187
191- // Extract the raw IEEE-754 bit pattern as a 64-bit integer (big-endian).
192- /** @var array{bits: int} $unpacked */
193- $ unpacked = unpack ('Jbits ' , pack ('E ' , $ value ));
194- $ bits = $ unpacked ['bits ' ];
188+ if (PHP_INT_SIZE >= 8 ) {
189+ // 64-bit: extract the IEEE-754 bit pattern as a 64-bit integer.
190+ /** @var array{1: int} $unpacked */
191+ $ unpacked = unpack ('J ' , pack ('E ' , $ value ));
192+ $ bits = $ unpacked [1 ];
195193
196- // Fields : [sign(1)|exp(11)|mantissa(52)]
197- $ signBit = ($ bits >> 63 ) & 1 ;
198- $ expBits = ($ bits >> 52 ) & 0x7FF ;
199- $ mantissa = $ bits & 0xFFFFFFFFFFFFF ;
194+ // Bits : [sign(1)|exp(11)|mantissa(52)]
195+ $ signBit = ($ bits >> 63 ) & 1 ;
196+ $ expBits = ($ bits >> 52 ) & 0x7FF ;
197+ $ mantissa = $ bits & 0xFFFFFFFFFFFFF ;
200198
201- // Zero (covers both 0.0 and -0.0).
202- if ($ expBits === 0 && $ mantissa === 0 ) {
203- return BigDecimal::zero ();
199+ // Zero (covers both 0.0 and -0.0).
200+ if ($ expBits === 0 && $ mantissa === 0 ) {
201+ return BigDecimal::zero ();
202+ }
203+
204+ if ($ expBits === 0 ) {
205+ $ significand = BigInteger::of ($ mantissa );
206+ } else {
207+ $ significand = BigInteger::of (0x10000000000000 | $ mantissa );
208+ }
209+ } else {
210+ // 32-bit: extract the IEEE-754 bit pattern as 8 bytes.
211+ $ packed = pack ('E ' , $ value );
212+
213+ // Get the first 16 bits as an integer.
214+ /** @var array{1: int} $unpacked */
215+ $ unpacked = unpack ('n ' , $ packed );
216+ $ header = $ unpacked [1 ];
217+
218+ // Bits: [sign(1)|exp(11)|mantissa(4)] in header (bytes 0-1) + 48 bits of mantissa in bytes 2-7
219+ $ signBit = ($ header >> 15 ) & 1 ;
220+ $ expBits = ($ header >> 4 ) & 0x7FF ;
221+ $ mantissaBytes = chr ($ header & 0x0F ) . substr ($ packed , 2 );
222+
223+ // Zero (covers both 0.0 and -0.0).
224+ if ($ expBits === 0 && $ mantissaBytes === "\x00\x00\x00\x00\x00\x00\x00" ) {
225+ return BigDecimal::zero ();
226+ }
227+
228+ $ mantissa = BigInteger::fromBytes ($ mantissaBytes , false );
229+
230+ if ($ expBits === 0 ) {
231+ $ significand = $ mantissa ;
232+ } else {
233+ // Implicit leading 1-bit (2^52).
234+ $ significand = $ mantissa ->plus (BigInteger::of (1 )->shiftedLeft (52 ));
235+ }
204236 }
205237
206238 if ($ expBits === 0 ) {
207239 // Subnormal: no implicit leading 1-bit; effective exponent = -1074.
208- $ significand = BigInteger::of ($ mantissa );
209240 $ baseExp = -1074 ;
210241 } else {
211- // Normal: implicit leading 1-bit (2^52).
212- $ significand = BigInteger::of (0x10000000000000 | $ mantissa );
213242 $ baseExp = $ expBits - 1075 ; // biased exp - 1023 (bias) - 52 (mantissa shift)
214243 }
215244
0 commit comments