Graymatter
Graymatter

Reputation: 6587

Constant single using the hex memory value in Delphi

How can I make a single constant based on a hex value where that hex value is an unsigned integer and the raw memory for the single. I would like to do something like this but it doesn't compile and this will try and cast the hex value to a single and then store the result of that cast instead of storing hex value itself:

LARGEST_SINGLE_LESS_THAN_ZERO = Single($80800000);

I get a "Invalid Typecast" error.

For example:

The single value for 1 is stored as $3F800000 in memory. I would like to be able to create a const that lets me set the value using $3F800000 instead of 1.

I have also tried other variations such as this without luck:

LARGEST_SINGLE_LESS_THAN_ZERO = PSingle(@$80800000)^;

Background

I have a method that I use to get the next smallest single when provided with a single value:

type
  PInt32 = ^Int32;

function NextBefore(const aValue: Single): Single;
var
  int32Value: Int32;
begin
  // this function ignores special values nan/inf
  int32Value := PInt32(@aValue)^;
  if (UInt32(int32Value) = $80000000) or (int32Value = 0) then
  begin
    // special handling needed for -0 and 0. We need to go to the smallest
    // negative number.
    int32Value := $80800000;
  end
  else
  begin
    if int32Value >= 0 then
      Dec(int32Value)
    else
      Inc(int32Value);
  end;
  Result := PSingle(@int32Value)^;
end;

This is really useful because we use vector operations that can only do a > or < so we use it to do the equivalent of a >= and a <=. We often check against 0. So where we need get all of the data >= 0 we do something like this:

MyVector.ThresholdGT(NextBefore(0));

It would be nicer to provide the other developers with a constant for these types of operations. Trying to use the PSingle format below won't work because the number is not a variable.

Upvotes: 1

Views: 1046

Answers (2)

David Heffernan
David Heffernan

Reputation: 612954

It's hard to do this cleanly with the constraints of the language. Perhaps the best that you can do is to make a variant record type that has both integer and single fields overlapped.

type
  TSingleIntegerVariantRec = record
    case Integer of
      0: (I: Integer);
      1: (S: Single);
  end;

Once you have that type available you can declare typed constants using the integer field, but then read the single field.

const
  LARGEST_SINGLE_LESS_THAN_ZERO: TSingleIntegerVariantRec = (I: $80800000);
.... 
MyVector.ThresholdGT(LARGEST_SINGLE_LESS_THAN_ZERO.S);

If you want to add an extra nuance you could implement an implicit cast operator to Single which would allow you to omit the .S. If you made that operator inline then I suspect the emitted code would be very efficient.

This does what you ask, but I wouldn't claim that it was very elegant. We're I you I would move the code to use the next value down into the library function so that you can pass 0 and shield the consumer of the library from these implementation details.

In other words you would add a ThresholdGTequal method that was implemented like this:

procedure TMyVector.ThresholdGTequal(const Value: Single);
begin
  ThresholdGT(NextBefore(Value));
end;

Then the consumers of this code simply write:

MyVector.ThresholdGTequal(0);

and remain oblivious to all of the gnarly implementation details.

Upvotes: 1

LU RD
LU RD

Reputation: 34899

In order to declare a single constant with a hex value in such a way that it cannot be altered by code, it can be done in two steps:

const 
  iLARGEST_SINGLE_LESS_THAN_ZERO : Int32 = $80800000; 
var 
  LARGEST_SINGLE_LESS_THAN_ZERO : Single absolute iLARGEST_SINGLE_LESS_THAN_ZERO;

Trying to change the value of LARGEST_SINGLE_LESS_THAN_ZERO will give a compiler error: Left side cannot be assigned to.

Upvotes: 2

Related Questions