PDA

View Full Version : Fixed point math



Legolas
31-05-2007, 12:11 PM
System: Windows XP sp2
Compiler: arm-nds-fpc 2.1.4. i386-win32
API: none

I need to do some conversion from floating point values to 1.19.12 fixed point. The code I have found in C is:

#define floattof32&#40;n&#41; &#40;&#40;int32&#41;&#40;&#40;n&#41; * &#40;1 << 12&#41;&#41;&#41;

that I have tried to convert in this way:

function floattof32(n: cfloat): cint32; inline;
begin
floattof32 := (cint32(n) * (1 shl 12));
end;

but does not work as I expected. Can anyone help me here?

Robert Kosek
31-05-2007, 12:58 PM
Heh, I had a whole reply typed up when I noticed something. Look at the C macro again for a second. The typecast is on the result of the shift and multiplication, and not the number before the multiplication. So your function should be:function floattof32(n: cfloat): cint32; inline;
begin
floattof32 := cint32((n) * (1 shl 12));
end;

Legolas
31-05-2007, 01:10 PM
Uh-oh... You are right... :oops:
However it does not work too

Robert Kosek
31-05-2007, 01:32 PM
Don't worry, I shot myself in the foot with a sizable if-then-else block the other day (just about driving myself nuts in the process). What exactly are you trying to do? I don't understand the multiplication in there. If you wanted to store the whole 32bit number as an integer you would call move() and shift the value over.

Legolas
31-05-2007, 01:45 PM
I need to store the floating point value in a 32 bit integer, where the 1st bit is the sign, bits from 2 to 20 are holding the integer part and 21 to 32 the fraction part. It's for 3d on Nintendo DS, because this particular format number is used to handle matrix transformations via hardware.

Robert Kosek
31-05-2007, 01:47 PM
Ouch. I really don't know how to help you then. Maybe someone else can who has a better knowledge of those things.

savage
31-05-2007, 01:54 PM
Do you have an example of type of input and expected output?

Legolas
31-05-2007, 02:09 PM
Do you have an example of type of input and expected output?

Thanks, mates. At this time I'm at work. As soon as I'll come back home I'll make it :)

savage
31-05-2007, 02:10 PM
btw, in Delphi 6 the previous code you posted does not compile as I get an invalid typecast error.

The following code appears to convert between the floats and Ints ( to a few decimal places ), but I'm not sure if it is what you are looking for...


function FloatToInt32(n: single): integer;
begin
result := Round((n) * (1 shl 12));
end;

function Int32ToFloat(n: integer): single;
begin
result := ( (n) / (1 shl 12) );
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage( FloatToStr( Int32ToFloat( FloatToInt32( 1.1 ) ) ) );
end;

Legolas
31-05-2007, 07:16 PM
I have tried every possible combination of trunc/round/shl/shr, but the obvious one:

function floattof32(n: cfloat): cint32; inline;
begin
floattof32 := trunc((n) * (1 shl 12));
end;

This one seems working as expected, though I have learned "never think that something works, even when it seems working". Thanks again, guys! And stay tuned, because I'm making some other strange type conversion routines :lol:

savage
31-05-2007, 07:26 PM
When I used trunc it gave me slight incorrect values converting back, but maybe that's ok for you.

Legolas
31-05-2007, 07:42 PM
Comparing C and Pascal results, all seems ok. I have preferred to use trunc because AFAIK it should be faster than round, at least on delphi/win32. On fpc/arm it should be faster too, I suppose :think:

pziko
31-05-2007, 11:39 PM
I think Round will be much faster (twice?) than Trunc because Trunc has to reset the FPU. Try each a couple of million times in a loop and see.

Mirage
01-06-2007, 05:54 AM
Round() much faster.
I also thought that Trunc() faster then Round() and now have to change a lot of code.:(

michalis
01-06-2007, 06:22 AM
Calling Trunc is doing exactly what the C code does. In C, typecasting float to integer causes automatic truncing (rounding to nearest integer towards zero, this is exactly what the Trunc in Pascal does).

BTW, what speed differences do you guys get with Delphi/win32 between Round and Trunc ?

I tested with FPC 2.0.4, Linux, i386, and got



Test Single&#58;
Trunc time&#58; 4.35
Round time&#58; 3.37
Test Double&#58;
Trunc time&#58; 4.58
Round time&#58; 3.37
Test Extended&#58;
Trunc time&#58; 5.82
Round time&#58; 5.08


which means that Round is indeed faster, but not terribly (and it matters only if you really do an awful lot of these calls, so it does not necessarily matter).

Legolas
01-06-2007, 06:48 AM
I think Round will be much faster (twice?) than Trunc because Trunc has to reset the FPU. Try each a couple of million times in a loop and see.

Uhm... I'm working on ARM cpu, where the FPU is not present. I'm wondering about the speed in this case :think:

Sly
02-06-2007, 06:39 AM
These might do a fair chunk of what you need. I've been playing around recently with fixed point math in C using templated classes. It is easier when the precision is the same for both fixed point numbers, but there are calculations for when both fixed point numbers have different precisions and you want the result in a third precision.

Edit: The FixedToInt function went missing, and the IntToFixed function had a typo.



type
Fixed = Integer;

const
Prec = 12;

function IntToFixed&#40;Value&#58; Integer&#41;&#58; Fixed;
begin
IntToFixed &#58;= Value shl Prec;
end;

function FixedToInt&#40;Value&#58; Fixed&#41;&#58; Integer;
begin
FixedToInt &#58;= Value shr Prec;
end;

function FloatToFixed&#40;Value&#58; Single&#41;&#58; Fixed;
begin
FloatToFixed &#58;= Round&#40;Value * &#40;1 shl Prec&#41;&#41;;
end;

function FixedToFloat&#40;Value&#58; Fixed&#41;&#58; Single;
begin
&#123; Multiply by 1.0 to convert to floating point before the divide &#125;
FixedToFloat &#58;= &#40;Value * 1.0&#41; / &#40;&#40;1 shl Prec&#41; * 1.0&#41;;
end;

function FixedAdd&#40;A, B&#58; Fixed&#41;&#58; Fixed;
begin
FixedAdd &#58;= A + B;
end;

function FixedSub&#40;A, B&#58; Fixed&#41;&#58; Fixed;
begin
FixedSub &#58;= A - B;
end;

function FixedMul&#40;A, B&#58; Fixed&#41;&#58; Fixed;
begin
FixedMul &#58;= &#40;A * B&#41; shr Prec;
end;

function FixedDiv&#40;A, B&#58; Fixed&#41;&#58; Fixed;
begin
FixedDiv &#58;= &#40;A shl Prec&#41; / B;
end;

Mirage
02-06-2007, 11:14 AM
Uhm... I'm working on ARM cpu, where the FPU is not present. I'm wondering about the speed in this case :think:

In that case Trunc() may be faster. It needs testing.

Legolas
02-06-2007, 11:33 AM
Sly: thanks a lot! That's gold for me :)

Sly
02-06-2007, 11:40 AM
I hope they help. They were coded in my head as I haven't actually done fixed-point in Pascal before (only done it in C). So I hope they work.