Typecasting Cardinal to Single
Asked Answered
S

4

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?

Spittoon answered 8/5, 2017 at 11:12 Comment(0)
F
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.

Flabby answered 8/5, 2017 at 11:29 Comment(0)
S
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

Snake answered 8/5, 2017 at 11:26 Comment(7)
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 answerFlabby
@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.Snake
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.Flabby
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.Spittoon
@Spittoon No problem :) You can use whichever solution you find most suitable in your case.Snake
Consideration about safety is a bit moot given the purpose is to get over what the compiler forbids.Receiptor
@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.Snake
C
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.

Calise answered 8/5, 2017 at 11:31 Comment(0)
S
3

You can use move to copy the raw bytes:

Assert(SizeOf(CardinalVar) = SizeOf(SingleVar);
Move(CardinalVar, SingleVar, SizeOf(CardinalVar));
Sonar answered 8/5, 2017 at 17:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.