3

I have a function that has a var Extended parameter. The compiler complains if I try to use a Double type argument when I call the function. However, if I pass the Extended value back as the function Result (and assign to a Double variable), then the compiler is happy.

Is this expected? If so, is there any way to fool the compiler to reduce the precision of the parameter to match the argument?

function foo1(var e: extended): boolean;
begin
  e := 0.0;
  Result := true;
end;

function foo2(): extended;
begin
  Result := 0.0;
end;

procedure CallFoo();
var
  d: double;
begin
  if foo1(d) then Exit; // compiler complains
  d := foo2; // compiler happy
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
PJH
  • 87
  • 5

1 Answers1

3

var parameters require the actual argument to match exactly. You can only pass an Extended variable.

One option for you is to introduce overloads for each of the floating point types you need. But my advice is to stop using Extended and switch to Double. The Extended type only exists on 32 Intel platforms and very seldom offers any benefit over Double. On the contrary, its unusual size of 10 bytes often leads to poor performance due to misalignment and inefficient cache usage.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Are you sure about the misalignment? The following simple declaration creates a record of 32 bytes in size, meaning the extended is aligned on a 16 byte boundary: `TestRec = record B: Byte; E: Extended; end;`. – Rudy Velthuis Mar 27 '17 at 17:36
  • @rudy Try an array of `Extended` – David Heffernan Mar 27 '17 at 17:40
  • @Rudy This answer has some good coverage of the topic: http://stackoverflow.com/questions/4583985/are-there-any-difference-between-array-and-packed-array-in-delphi Interestingly, as I read what Barry says, Extended has alignment of 8 so your E should be at offset 8, there should be 6 bytes of padding at the end and the size of the record should be 24. – David Heffernan Mar 27 '17 at 17:47
  • I always use `{$ALIGN 16}`, so it was 32 alright. At the time Barry wrote that, 16 byte alignment was not available yet. But I looked at several arrays of Extended, and indeed, the second Extended in each array was aligned on a 2 byte (or 10 byte) boundary, i.e. the hex address ended in 2 or A. Ouch! To get proper alignment (with 16 byte alignment set), you would have to put them in a record and have an array of such records, I guess. Not good. – Rudy Velthuis Mar 27 '17 at 17:56
  • @rudy Even if you do that you still waste 6 bytes for every value and burn through cache which is critical very often. Extended should not be used. We can live without it on x64, let's live without it everywhere. – David Heffernan Mar 27 '17 at 17:58
  • Just tried the `array of record E: Extended end` thing. Even then, the Extendeds are aligned on 8 byte boundaries, not on 16 byte boundaries. So you are indeed right. That's not good. – Rudy Velthuis Mar 27 '17 at 18:00
  • An advice at the filler part of answer is actually pretty bad. – Free Consulting Mar 27 '17 at 18:02
  • @David: Taking into consideration what I just tried, you are right, indeed. But many RTL routines still use Extended internally. – Rudy Velthuis Mar 27 '17 at 18:02
  • 1
    @FreeConsulting: I don't quite understand. What "filler part of answer" are you talking about? – Rudy Velthuis Mar 27 '17 at 18:04
  • 1
    @Rudy for the return value it comes back in st(0) so it doesn't much matter. In other cases it's best to hide the RTL functions with pure double versions. But the more important thing is not to use Extended as your data type for variables. – David Heffernan Mar 27 '17 at 18:07
  • @Rudy, I mean answer of course. SO disallows too short answers. – Free Consulting Mar 27 '17 at 18:13
  • 1
    @Free You clearly have no idea what you are talking about and no understanding of issues such as alignment and cache waste. Are you an expert on floating point? Or do you just like stirring? – David Heffernan Mar 27 '17 at 18:15
  • Issues you're urging to worry about are completely unrelated to the question, that is, *a filler*. – Free Consulting Mar 27 '17 at 18:26
  • 1
    I was responding to your comment that the advice was poor. I don't believe you know what you are talking about, I don't believe you appreciate how important alignment and efficient cache usage is. – David Heffernan Mar 27 '17 at 18:38
  • Thank you David. The overload seems to be the best option as I am working with a 3rd party component that provides extended as a user setting. I am still a bit surprised that the compiler treats the function return differently to a var parameter. – PJH Mar 27 '17 at 19:04
  • Thank you for sharing *your beliefs* but they are unrelated to the pretty specific question. @PJH, your cases are very different types of expression, see http://docwiki.embarcadero.com/RADStudio/Seattle/en/Parameters_(Delphi)#Value_and_Variable_Parameters – Free Consulting Mar 27 '17 at 21:42
  • They aren't beliefs. Demonstrable fact. – David Heffernan Mar 27 '17 at 21:45
  • 2
    @Free - Documentation you linked cites: *"If a routine's declaration specifies a var parameter, you must pass an assignable expression"*. Obviously a double is assignment compatible to an extended. Is there anything on that page that explains the behavior in the question? – Sertac Akyuz Mar 27 '17 at 22:44
  • 1
    Topic for "E2033" is accurate though: *"For a variable parameter, the actual argument must be of the exact type of the formal parameter."*. – Sertac Akyuz Mar 27 '17 at 22:56
  • Don't hesitate to demonstrate. why waffle... @Sertac, that page explains value and reference parameter but doesn't forbids an implicit type conversion, this one does - http://docwiki.embarcadero.com/RADStudio/Seattle/en/Calling_Procedures_and_Functions and assignment statement http://docwiki.embarcadero.com/RADStudio/Seattle/en/Type_Compatibility_and_Identity#Assignment_Compatibility allows certain liberties. – Free Consulting Mar 27 '17 at 23:21
  • @FreeConsulting - thanks for the documentation links, which explain the rules clearly. However, I am still a bit mystified by why the rules for var parameters are stricter than for function result assignment. Any ideas? – PJH Mar 28 '17 at 12:17
  • 1
    @PJH, well, I'd say otherwise - assignment statement (the fact what right-side expression comes from function call is irrelevant) is more liberal than the usual strict rules. The first page covers that: with `var` you pass a reference to variable of agreed-upon type, so the function writes directly there w/o any type conversion magic inherent to assignment statement. From the same page: *A variable parameter, on the other hand, acts like a pointer rather than a copy.* – Free Consulting Mar 28 '17 at 13:18
  • @PJH: A var parameter is an address. At the time the function is compiled, it can impossibly know what you will pass later. Conversion is not possible too, because the function might *store* something at the address passed. And that must be *exactly* compatible with the declared type, otherwise it would e.g. try to store a 10 byte extended in a location that is only meant to contain a double (8 bytes in size). That is why it is not allowed. Even if the size were the same, the structure of the value could be very different (e.g. storing a single in a location meant to contain an integer). – Rudy Velthuis Mar 28 '17 at 22:34