Migrating Delphi Win32 Extended data type in a Win64 application/environment

183 Views Asked by At

I have a file which was created by a Delphi (version unknown) Win32 application using the 10-byte Extended data type. I need to be able to read this data type (from the file) in a new application (I'm writing in Delphi 11 using FMX) and convert to an "equivalent" 8-byte value (accepting the loss of precision). I'm assuming reading from the file may need to occur on a byte-by-byte basis as the 10-byte data blocks are not 8-byte boundary aligned.

My understanding is that the 10-byte Extended type is not supported by Win64 (Extended in Win64 is 8 bytes). I understand there is a TExtended80Rec type and TExtendedHelper.

Any suggestions on how to do this?

Looking forward (yes, this is probably a 2nd (but related) question), as to avoid this issue in the future (in particular as the application is intended to be cross-platform compatible): is there a way to ensure that all data types are defined in a consistent manner (in terms of number of bytes) independent of target platform (I'm looking at Win32, Win64, macOS, and macOS ARM targets initially, and potentially Linux as well further down the track)? This is particularly important so that files created on one platform can be imported into the same application on any other platform.

2

There are 2 best solutions below

0
David Heffernan On BEST ANSWER

You can use the RTL helper record TExtended80Rec. This is a record with a size of 10 bytes, that allows you to manipulate 80 bit floats.

You can read your data into a value of this type, and then use the explicit cast operator to Extended to get the actual value.

You want to be using Double in your code, which is supported on all platforms.

Here's a very simple helper function that will read an 80 bit value from a stream, and yield the corresponding Double value.

function Read80BitExtended(Stream: TStream): Double;
var
  Value: TExtended80Rec;
begin
  Stream.ReadBuffer(Value, SizeOf(Value));
  Result := Extended(Value);
end;

One thing to be aware of is that you might encounter values that are outside the range of Double. In which case a floating point overflow error will be generated. This may trigger a runtime exception is the floating point overflow error is unmasked, as it is by default in versions before 12. But in version 12, where floating point exceptions are masked by default, there would be no exception raised.

0
AmigoJack On

Reading raw Extended

The documentation for TExtended80Rec explains:

#else /* _WIN64 */
  unsigned __int64 aExtended80Frac;
  Word aExtended80Exp;

This means you can use this record to read 8+2=10 bytes off a file, unrelated for which target platform your executable is compiled.

var
  F: TExtended80Rec;
  E: Extended;
begin
  ...
  E := Extended(F);

Read SizeOf( F ) bytes from your file (should always compute to 10), then assign that variable's content to any other variable - either of type Extended or Double (or Single). Cannot test or verify this.

Keep in mind that you can still compile a separate 32 bit program which reads your old file perfectly (since it still acts on 10 bytes), unbound to the platform of the operating system you're running (f.e. Windows 10x64) and to store/convert it in/to an intermediate/recent format which your up-to-date program can also read.

More compatible data types

Data types Double and Single are known to many programming languages and are the industry standard IEEE_754-1985. You can rely on storing both, since with the standard you will always know how to read them in the future again, even if Delphi or any other compiler choses to treat them differently.

Your own approach/data type

Most future proof would be to store it by your own standard, using Integers of never changing sizes only, f.e.:

  • 4 bytes sign/mantissa (1 Cardinal)
  • 4 bytes exponent (1 Cardinal)
  • 8 bytes fraction (2 Cardinals)

Alternative approaches are to store fractions instead of decimal fractions - this is done in video/photo occasionally (since you want human readible values like 16/9 anyway), f.e.:

  • 4 bytes numerator (1 Cardinal)
  • 4 bytes denominator (1 Cardinal)

You have to decide for yourself if storing and reading the data has the highest priority (but interpreting/using it is costly), or the precision has the highest priority (making use of it is a different topic), or if it's just about compatibility (then you don't care that much about precision or performance).