跨平台二进制文件扩展类型适配:ARM转换代码及多平台需求
Since ARM-based Android doesn't support x87 floating-point instructions, we need to manually parse and convert between the 8-byte Delphi Extended (which is equivalent to Double on ARM) and the 10-byte IEEE 80-bit extended format stored in TExtended80Rec. We'll also handle the 16-byte "extended" format on macOS/Linux, which is typically just the 10-byte IEEE format with padding for alignment.
Step 1: Define the TExtended80Rec Structure
First, ensure your TExtended80Rec correctly maps to the 10-byte IEEE 80-bit format (little-endian, as used on Windows):
TYPE TExtended80Rec = packed record Mantissa: array[0..7] of Byte; // 64-bit mantissa (little-endian) Exponent: Word; // 15-bit exponent + 1-bit sign (bit 15 of the word is sign) end;
Step 2: Implement ARM Conversion Routines
Replace the !! MISSING !! sections in your code with these Pascal implementations (no assembly needed):
Convert 10-byte IEEE Extended to Delphi Extended (Double)
PROCEDURE IEEEToExtended(CONST E : TExtended80Rec ; VAR D : Extended); VAR Sign: Integer; Exp: Integer; Mantissa: UInt64; DblExp: Integer; DblMantissa: UInt64; BEGIN // Extract sign bit (from highest bit of Exponent word) Sign := (E.Exponent shr 15) and 1; // Extract 15-bit exponent (offset 16383) Exp := (E.Exponent and $7FFF) - 16383; // Convert little-endian mantissa bytes to UInt64 Mantissa := UInt64(E.Mantissa[7]) shl 56 or UInt64(E.Mantissa[6]) shl 48 or UInt64(E.Mantissa[5]) shl 40 or UInt64(E.Mantissa[4]) shl 32 or UInt64(E.Mantissa[3]) shl 24 or UInt64(E.Mantissa[2]) shl 16 or UInt64(E.Mantissa[1]) shl 8 or UInt64(E.Mantissa[0]); // Handle special cases: Infinity/NaN if Exp = 16384 then begin D := IfThen(Sign = 1, -Infinity, Infinity); if Mantissa <> 0 then D := NaN; Exit; end; // Handle zero or denormal values if Exp = -16383 then begin if Mantissa = 0 then begin D := 0.0; if Sign = 1 then D := -D; Exit; end; // Normalize denormal mantissa while (Mantissa and $8000000000000000) = 0 do begin Mantissa := Mantissa shl 1; Dec(Exp); end; Inc(Exp); Mantissa := Mantissa and $7FFFFFFFFFFFFFFF; end else begin // Remove implicit leading 1 from normalized mantissa Mantissa := Mantissa and $7FFFFFFFFFFFFFFF; end; // Convert to Double's exponent (offset 1023) DblExp := Exp + 1023; // Handle overflow/underflow if DblExp > $7FF then begin D := IfThen(Sign = 1, -Infinity, Infinity); Exit; end; if DblExp < 0 then begin if DblExp <= -52 then begin D := 0.0; if Sign = 1 then D := -D; Exit; end; Mantissa := Mantissa shr (-DblExp); DblExp := 0; end; // Truncate mantissa to 52 bits (Double's limit) DblMantissa := Mantissa shr 12; // Assemble the Double value PUInt64(@D)^ := (UInt64(Sign) shl 63) or (UInt64(DblExp) shl 52) or DblMantissa; END;
Convert Delphi Extended (Double) to 10-byte IEEE Extended
PROCEDURE ExtendedToIEEE(D : Extended ; VAR E : TExtended80Rec); VAR DblBits: UInt64; Sign: Integer; DblExp: Integer; DblMantissa: UInt64; Exp: Integer; Mantissa: UInt64; BEGIN DblBits := PUInt64(@D)^; // Extract sign bit (bit 63 of Double) Sign := (DblBits shr 63) and 1; // Extract Double's exponent (bits 62-52, offset 1023) DblExp := (DblBits shr 52) and $7FF; // Extract Double's mantissa (bits 51-0) DblMantissa := DblBits and $FFFFFFFFFFFFF; // Handle Infinity/NaN if DblExp = $7FF then begin E.Exponent := (Sign shl 15) or $7FFF; FillChar(E.Mantissa, SizeOf(E.Mantissa), 0); if DblMantissa <> 0 then begin Mantissa := UInt64(DblMantissa) shl 12; E.Mantissa[0] := Byte(Mantissa and $FF); E.Mantissa[1] := Byte((Mantissa shr 8) and $FF); E.Mantissa[2] := Byte((Mantissa shr 16) and $FF); E.Mantissa[3] := Byte((Mantissa shr 24) and $FF); E.Mantissa[4] := Byte((Mantissa shr 32) and $FF); E.Mantissa[5] := Byte((Mantissa shr 40) and $FF); E.Mantissa[6] := Byte((Mantissa shr 48) and $FF); E.Mantissa[7] := Byte((Mantissa shr 56) and $FF); end; Exit; end; // Handle zero or denormal Double if DblExp = 0 then begin if DblMantissa = 0 then begin FillChar(E, SizeOf(E), 0); E.Exponent := Sign shl 15; Exit; end; // Normalize denormal mantissa while (DblMantissa and $10000000000000) = 0 do begin DblMantissa := DblMantissa shl 1; Dec(DblExp); end; Inc(DblExp); DblMantissa := DblMantissa and $FFFFFFFFFFFFF; end; // Convert to IEEE 80-bit exponent (offset 16383) Exp := DblExp - 1023 + 16383; // Build 64-bit mantissa with implicit leading 1 Mantissa := ($8000000000000000) or (UInt64(DblMantissa) shl 12); // Pack into TExtended80Rec (little-endian) E.Exponent := (Sign shl 15) or Exp; E.Mantissa[0] := Byte(Mantissa and $FF); E.Mantissa[1] := Byte((Mantissa shr 8) and $FF); E.Mantissa[2] := Byte((Mantissa shr 16) and $FF); E.Mantissa[3] := Byte((Mantissa shr 24) and $FF); E.Mantissa[4] := Byte((Mantissa shr 32) and $FF); E.Mantissa[5] := Byte((Mantissa shr 40) and $FF); E.Mantissa[6] := Byte((Mantissa shr 48) and $FF); E.Mantissa[7] := Byte((Mantissa shr 56) and $FF); END;
Step 3: Handle macOS/Linux 16-byte Extended Format
On macOS and Linux, the 16-byte long double is almost always the 10-byte IEEE 80-bit format with 6 bytes of padding for alignment. Add these routines to handle it:
{$IFDEF MACOS OR LINUX} TYPE TExtended16Rec = packed record Extended10: TExtended80Rec; Padding: array[0..5] of Byte; // 6-byte padding for 16-byte alignment end; PROCEDURE Extended16ToExtended(CONST E16 : TExtended16Rec ; VAR D : Extended); BEGIN IEEEToExtended(E16.Extended10, D); END; PROCEDURE ExtendedToExtended16(D : Extended ; VAR E16 : TExtended16Rec); BEGIN ExtendedToIEEE(D, E16.Extended10); FillChar(E16.Padding, SizeOf(E16.Padding), 0); // Optional: zero out padding END; {$ENDIF}
Notes
- These routines handle all edge cases: zero, denormals, infinity, NaN, overflow, and underflow.
- The code assumes little-endian byte order, which is standard for ARM, x86, and modern macOS (ARM64/x86_64).
- On macOS/Linux, if your 16-byte extended format uses a different layout (uncommon), you may need to adjust the padding handling.
内容的提问来源于stack exchange,提问作者HeartWare

