Recent

Author Topic: specifying the data type  (Read 4859 times)

Seenkao

  • Hero Member
  • *****
  • Posts: 751
    • New ZenGL.
specifying the data type
« on: April 01, 2026, 02:37:59 pm »
Hi All!
Я указываю данным как их использовать в данный момент времени. Но при указании данным, что они будут использоваться как Single, FPC преобразует данные для дальнейшего использования (использует cvtsi2ss), а не берёт уже готовые данные.
Почему так? И как мне решить подобную проблему.


Google translate:
I tell the data how to use it at a given moment in time. But when specifying data that it will be used as Single, FPC converts the data for further use (uses cvtsi2ss), rather than taking ready-made data.
Why is this? And how can I solve this problem?

Code: Pascal  [Select][+][-]
  1. var
  2.   v: single;
  3. begin
  4.   ...
  5.   v := Single(Cardinal(v) xor $80000000);       // here used cvtsi2ss
  6.   ...
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: specifying the data type
« Reply #1 on: April 01, 2026, 03:17:12 pm »
Code: Pascal  [Select][+][-]
  1. var
  2.   v: single;
  3. begin
  4.   ...
  5.   v := Single(PCardinal(@v)^ xor $80000000);

This is not my favorite programming style in Pascal, beware!!!!

There is one important thing to note about your original attempt:
FPC converts the value first to its result type and that does not compare to a bit pattern.
If you do not mean that, my code is invalid, because that uses the bit pattern without conversion, like C(++) does.

This is a fundamental difference between FPC Pascal and most other languages.

My example is only useful when you need to interpret code or mimic code from other languages like C(++) that use the bit pattern directly.

In your case, my code is probably correct.
It is also an example how to translate such code from C to Pascal.
« Last Edit: April 01, 2026, 03:33:10 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Seenkao

  • Hero Member
  • *****
  • Posts: 751
    • New ZenGL.
Re: specifying the data type
« Reply #2 on: April 01, 2026, 04:43:45 pm »
Thaddy, благодарю за ответ, но суть больше не в этом.
При работе с данными, мы можем указать с какими данными мы работаем. Например код что ниже.
Но это не действует когда мы указываем что данные будут использоваться как Single. И я бы понял, если бы я указал значение s := n (где s - Single, n - Longword) - что здесь делается преобразование. Но я указал тип использования данных и в случае s := Single(n) - преобразований не должно быть!


Google translate:
Thaddy, thanks for the reply, but that's not the point.
When working with data, we can specify what kind of data we're working with. For example, the code below.
But this doesn't work when we specify that the data will be used as Single. And I would have understood that if I had specified the value s := n (where s is Single, n is Longword)—that a conversion is being performed here. But I specified the data usage type, and in the case of s := Single(n)—no conversion should occur!

Code: Pascal  [Select][+][-]
  1. var
  2.   n, a: LongWord;
  3.   m: byte;
  4. type
  5.   myType = record
  6.     y, w, h, x: byte;
  7.   end;
  8.  
  9. begin
  10.   ...
  11.   n := $7B848DDA;
  12.   m := byte(n);
  13.   m := myType(n).x;
  14.   ...
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: specifying the data type
« Reply #3 on: April 01, 2026, 07:19:34 pm »
You misunderstood me:
Due to the way FPC handles singles, the only way I know to treat a hex value as a single is as I described in my previous post.
So with a pointer cast to the literal and then a single cast to dereference the value.
This is documented in  one of the release notes, but I have to look that up which one and I will add it later. (I think it was in 3.0.0)
Example:
Code: Pascal  [Select][+][-]
  1. var
  2.   v: single = $55555555;
  3. begin
  4.   writeln(v);  // prints the single
  5.   v := Single(PCardinal(@v)^);// same value
  6.   writeln(v);
  7.   v := Single(PCardinal(@v)^ xor $80000000); // performs the xor
  8.   writeln(v); // different value
  9. end.
[EDIT] since single is a value type, the following would therefor fail:
Code: Pascal  [Select][+][-]
  1. var v:single = $55555555;
  2. begin
  3.      v := single(cardinal(v) xor  $80000000));
  4. end.
It is documented that this is the case, as is the solution I suggested:
You cast a pointer to the address and perform bitwise operations by dereferencing it. That is the only way to perform bitwise operations on a value type.
« Last Edit: April 02, 2026, 06:30:16 am by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: specifying the data type
« Reply #4 on: April 02, 2026, 07:17:39 am »
Since this is a recurring issue, let me explain more in-depth:

Somewhere in the past fpc strengthened its type system.
In older versions fpc did not convert the single to an integer type, but you could directly manipulate the bit pattern that you tried to achieve with:
v := Single(Cardinal(v) xor $80000000); // worked, but not any more.
This is called a reinterpret cast (like C):
Code: C  [Select][+][-]
  1. float f = ...;
  2. uint32_t bits = *(uint32_t*)&f;

But once single became a proper value type, the compiler had to enforce conversions instead of reinterpretations.
And that is why you need to dereference the value type by taking its address and dereference that like so:
Code: Pascal  [Select][+][-]
  1. v := Single(PCardinal(@v)^ xor $80000000); // flips sign bit
This in effect makes sure that the compiler does not perform the conversion first. (what is what the compiler with cardinal(c) does)

So my first answer is really the correct solution.
« Last Edit: April 02, 2026, 07:35:30 am by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Khrys

  • Sr. Member
  • ****
  • Posts: 439
Re: specifying the data type
« Reply #5 on: April 02, 2026, 07:32:02 am »
Но я указал тип использования данных и в случае s := Single(n) - преобразований не должно быть!

Google translate:
But I specified the data usage type, and in the case of s := Single(n)—no conversion should occur!

Casts into floating-point types  (Single, Double, Extended)  behave differently from "normal" casts.
Yes, this is inconsistent - but I can't think of any language that doesn't have this inconsistency:

Code: C  [Select][+][-]
  1. int i = 42;          // This also works in Java!
  2. float f = (int) i;   // cvtsi2ss -> 42.0f

Even Rust, known for its extraordinary focus on correctness & memory safety, behaves like this:

Code: C++  [Select][+][-]
  1. let i: i32 = 42;
  2. let f: f32 = i as f32; // cvtsi2ss -> 42.0f32

However, casts from floating-point types do not perform any conversions in FPC - unlike the aforementioned languages.
I think this is the real inconsistency here: not only does the behavior of casts differ between types, but that behavior isn't even symmetric, thus turning into a horrible footgun:

Code: C  [Select][+][-]
  1. float x = 1.5;
  2. int n = (int) x;     // cvtss2si -> 1
  3. printf("%d\n", n);   // Prints "1"

Code: Pascal  [Select][+][-]
  1. var X: Single = 1.5;
  2. var N: Integer = Integer(X);
  3. WriteLn(N); // Prints "1069547520" aka 0x3FC00000

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: specifying the data type
« Reply #6 on: April 02, 2026, 07:41:32 am »
Yes, this is inconsistent - but I can't think of any language that doesn't have this inconsistency:
No it is not. Read my reply above:
It is consistent, since floats in FreePascal are proper value types, so cardinal(v) converts to cardinal, not reinterpret the value, whereas PCardinal(*v)^ uses the address directly.

And it is really only because FPC strengthened its type system.
This is a good thing and perfectly consistent.

What it is, is a trap for people coming from languages that do not have a strong type system, like C, and try to translate C code to Pascal.
Then it bites.

 A cleaner way to flip the sign is simply v := v * -1  or
Code: Pascal  [Select][+][-]
  1. var
  2.   v: single = pi;
  3.   c: cardinal absolute v;
  4. begin
  5.   writeln ('default value:      ', v);
  6.   c := c xor $80000000;
  7.   writeln(v);
  8. end.
You can also verify that v := v * -1 generates the exact same assembler as the casting fest. I used {$I-}{$OPTIMIZATION LEVEL4}

Languages with proper value types like Freepascal include C#, Java, Kotlin, Python, Haskell, Ada. But not C, C++ and the likes.
(And Rust and Go without unsafe also use proper value types).

To summarize: to flip the sign as in the original question, simply multiply by -1 otherwise use pointer dereferences.
« Last Edit: April 02, 2026, 08:59:25 am by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: specifying the data type
« Reply #7 on: April 02, 2026, 09:17:28 am »
But I specified the data usage type, and in the case of s := Single(n)—no conversion should occur!
To answer to your second post:
But your code is correct except you did not pack the record to byte alignment and without pack settings:
Code: Pascal  [Select][+][-]
  1. {$packrecords 1} // which is the default packing, otherwise it is aligned to pack settings and that may differ.
  2. var
  3.   n, a: LongWord;
  4.   m: byte;
  5. type
  6.   myType = packed record
  7.     y, w, h, x: byte;
  8.   end;
  9.  
  10. begin
  11.   n := $7B848DDA;
  12.   m := byte(n);
  13.   writeln($DA:4,m:4);
  14.   m := myType(n).x;
  15.   writeln($7B:4,m:4);
  16. end.
Were you maybe confused that storage is byte reversed on most systems (LB)? Maybe you expected  byte(n shr 24); which is also 123

More in general, single does not need type conversion for ordinal values as in:
Code: Pascal  [Select][+][-]
  1.   writeln(single(m):10,single(pbyte(@m)^):10);// same values
but that is also consistent with the type system.

« Last Edit: April 02, 2026, 09:43:57 am by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Seenkao

  • Hero Member
  • *****
  • Posts: 751
    • New ZenGL.
Re: specifying the data type
« Reply #8 on: April 02, 2026, 04:25:17 pm »
Code: Pascal  [Select][+][-]
  1. var
  2.   v: single;
  3. begin
  4.   ...
  5.   v := Single(PCardinal(@v)^ xor $80000000);
Ваш код не работает.  :(  Преобразование данных остаётся.
Остаётся вариант умножения на (-1).

Google translate:
Your code doesn't work. :(   Data conversion remained the same.
The only option left is to multiply by (-1).

Quote
Were you maybe confused that storage is byte reversed on most systems (LB)? Maybe you expected  byte(n shr 24); which is also 123
No. I know about this, but usually I work directly with memory this way.
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

ALLIGATOR

  • Sr. Member
  • ****
  • Posts: 417
  • I use FPC [main] 💪🐯💪
Re: specifying the data type
« Reply #9 on: April 02, 2026, 05:31:07 pm »
Code: Pascal  [Select][+][-]
  1. program app;
  2. {$mode objfpc}
  3. {$optimization on}
  4.  
  5. procedure kek;
  6. var
  7.   v, r: single;
  8.   tmp: cardinal;
  9. begin
  10.   tmp := Cardinal(v) xor $80000000;
  11.   v := PSingle(@tmp)^ * 1.5;
  12. end;
  13.  
  14. begin
  15.   kek;
  16. end.
  17.  
I may seem rude - please don't take it personally

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: specifying the data type
« Reply #10 on: April 02, 2026, 05:36:41 pm »
Well, what does not work? All my full examples are tested.
Show me what does not work, please.
I have been using code like that for 20+ years.

My code works on all platforms unless you use a very old FPC version, but then your initial cast would have worked.
See ALLIGATOR's example which is the basically the same.
(and he should be a little ashamed about the temp  :-X )

Anyway, for flipping the sign bit, v:= v * -1 is much more elegant and really! the compiler generates the same code as the pointer shenanigans.

Teaser: v *= -1 ;  :D if you want to stick to C like programming style.
« Last Edit: April 02, 2026, 05:52:56 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Seenkao

  • Hero Member
  • *****
  • Posts: 751
    • New ZenGL.
Re: specifying the data type
« Reply #11 on: April 02, 2026, 06:17:08 pm »
Well, what does not work? All my full examples are tested.
Free Pascal Compiler version 3.2.2-r0d122c49 for x86_64

v := Single(PCardinal(@v)^ xor $80000000);     -> used cvtsi2ss
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: specifying the data type
« Reply #12 on: April 02, 2026, 06:33:46 pm »
So it is a cpu/fpu type issue? You want to avoid such instructions?
That can be done, but which instruction set is acceptable to you?
(How low? or maybe how high?)
The options are: -Cp<cputype>, -Cf<fputype>
You can see the options for your compiler by typing fpc -i or fpc -if or fpc -ic.

This particular instruction is supported by intel and amd for many years.
As far as I can see it is also correct. Show me the output and show me what you expect. The instruction is correct if supported:cvtsi2ss
« Last Edit: April 02, 2026, 06:42:31 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Seenkao

  • Hero Member
  • *****
  • Posts: 751
    • New ZenGL.
Re: specifying the data type
« Reply #13 on: April 02, 2026, 06:42:02 pm »
Решение, по сути, уже найдено. И в настоящее время, похоже оно лучшее.
Если я буду что-то на ассемблере делать, то вряд ли я буду использовать подобные решения и вряд ли буду смешивать языки сильно, особенно в функциях/процедурах.

Google translate:
A solution has essentially already been found. And at the moment, it appears to be the best one.
If I'm going to do something in assembler, I'm unlikely to use solutions like these and I'm unlikely to mix languages ​​much, especially in functions/procedures.
Quote
v:= v * -1
Rus: Стремлюсь к созданию минимальных и достаточно быстрых приложений.

Eng: I strive to create applications that are minimal and reasonably fast.
Working on ZenGL

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: specifying the data type
« Reply #14 on: April 02, 2026, 06:43:23 pm »
 ;) :) Ok. I will stop investigating.
objects are fine constructs. You can even initialize them with constructors.

 

TinyPortal © 2005-2018