10

Both Cardinal and Single are 4 byte / 32 bit datatypes, but when I typecast them to each other I get an Invalid Typecast error in Delphi 10.1 (Berlin).

lSingleVar := Single(lCardinalVar);

I am NOT talking about converting between the two types, as that will only store 23 bits of the cardinal data (the fraction part of the Single datatype). I need to store minimum 30 bits of data into a Single variable. I have good reasons for that (the type cannot be changed), which I will be pleased to elaborate on.

How do I typecast a Single variable?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hans
  • 2,220
  • 13
  • 33

4 Answers4

12

I would do it like this:

lSingleVar := PSingle(@lCardinalVar)^;

And in the opposite direction it would be:

lCardinalVar := PCardinal(@lSingleVar)^;

I cannot think of a more direct way to achieve what you request. To my mind this has the advantage of not requiring any type or function definitions.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
9

You can use a variant record to access the same underlying memory:

type
  TConverter = record
  case integer of
    0 : (c: cardinal);
    1 : (s: single);
  end;

And then use it like this:

var
  converter: TConverter;
  lCardinalVar: cardinal;
  lSingleVar: single;

  converter.c := lCardinalVar;
  lSingleVar := converter.s;

or with a single line typecast like this:

lSingleVar := TConverter(lCardinalVar).s

Variant parts in records

psmears
  • 26,070
  • 4
  • 40
  • 48
Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159
  • 2
    You can avoid the local variable by writing `lSingleVar := TConverter(lCardinalVar).s;` and the compiler will emit a simple mov operation, just the same as it would emit for the code in my answer – David Heffernan May 08 '17 at 12:06
  • 1
    @DavidHeffernan of course. I included your suggestion. IMO, example with local variables is usually easier to understand and makes inner workings of variant parts more obvious, so I will leave it there. – Dalija Prasnikar May 08 '17 at 13:08
  • 1
    Yeah, any of these methods are basically fine, and personal preference matters. Actually, one advantage of the cast approach that you added is that the compiler verifies that the variable being cast is the same size of the converter type. – David Heffernan May 08 '17 at 13:56
  • I like your solution because it is safe and I find it to be the most correct way to do it. However, I ended up using the solution suggested by David because it can be done on the spot without defining anything elsewhere. – Hans May 09 '17 at 07:09
  • 1
    @Hans No problem :) You can use whichever solution you find most suitable in your case. – Dalija Prasnikar May 09 '17 at 07:40
  • Consideration about safety is a bit moot given the purpose is to get over what the compiler forbids. – Sertac Akyuz May 09 '17 at 17:17
  • @SertacAkyuz Generally speaking, when you are aiming at your foot you can either shoot it or you can blast it away :) Even when you are hacking, you might want to use safest viable approach. – Dalija Prasnikar May 09 '17 at 18:34
6

You can write a function like this:

function ConvertCardinalToSingle(value: Cardinal): Single;
var AsSingle: Single absolute value;
begin
  Result := AsSingle;
end;

Here we use absolute keyword which means: variable value and AsSingle allocate the same memory. This keyword is considered obsolete by many, and it's definitely 'unsafe', but it has its uses (I like to use it in event handlers to cast Sender to the type I need, but first checking anyway).

You don't have to write a function, you can just have these two variables pointing to one place at some point.

Yuriy Afanasenkov
  • 1,440
  • 8
  • 12
3

You can use move to copy the raw bytes:

Assert(SizeOf(CardinalVar) = SizeOf(SingleVar);
Move(CardinalVar, SingleVar, SizeOf(CardinalVar));
dummzeuch
  • 10,975
  • 4
  • 51
  • 158