简体   繁体   中英

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.

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM