Jump to content


Fast int<-->float conversion routines?


24 replies to this topic

#1 Nautilus

    Senior Member

  • Members
  • PipPipPipPip
  • 358 posts

Posted 24 June 2007 - 10:34 PM

Greetings,
I'm looking for conversion routines [int <--> float] faster
than the standard cast ops int() and float().

I can write my own in C++, but I need to best the standard
cast ops. Instead I barely match them.

Looking for material, the best I've found is Agner Fog's
"float to int" rounding routines, but I'm interested in
truncation, not rounding.
And there's no "int to float" conversion anyway.

Other links I've found either point me to libraries to license,
or quick explanations about why the standard cast ops
should be avoided when performance is of importance, and
not a line of usable code.

Please, do you have any valid link?

Ciao ciao : )
-Nautilus

(readin' this? you ought to get out more)


#2 kusma

    Valued Member

  • Members
  • PipPipPip
  • 163 posts

Posted 24 June 2007 - 10:55 PM

http://www.xyzw.de/c190.html has some float->int stuff.

#3 anubis

    Senior Member

  • Members
  • PipPipPipPip
  • 2225 posts

Posted 25 June 2007 - 09:17 AM

Nautilus said:

Greetings,
I'm looking for conversion routines [int <--> float] faster
than the standard cast ops int() and float().

I can write my own in C++, but I need to best the standard
cast ops. Instead I barely match them.

Looking for material, the best I've found is Agner Fog's
"float to int" rounding routines, but I'm interested in
truncation, not rounding.
And there's no "int to float" conversion anyway.

Other links I've found either point me to libraries to license,
or quick explanations about why the standard cast ops
should be avoided when performance is of importance, and
not a line of usable code.

Please, do you have any valid link?

Ciao ciao : )

If I remember correctly the cast ops are slow because they actually result in a function call to ftol. Simply write a piece of assembly code that pops the float you want from the floating point stack. That will atuomatically truncate it. I can post a piece of code when I'm home from work in a few hours or so.
If Prolog is the answer, what is the question ?

#4 Nils Pipenbrinck

    Senior Member

  • Members
  • PipPipPipPip
  • 597 posts

Posted 25 June 2007 - 12:07 PM

This one might be interesting for you as well:

http://www.stereopsis.com/FPU.html
My music: http://myspace.com/planetarchh <-- my music

My stuff: torus.untergrund.net <-- some diy electronic stuff and more.

#5 Nils Pipenbrinck

    Senior Member

  • Members
  • PipPipPipPip
  • 597 posts

Posted 25 June 2007 - 12:12 PM

Also, if you know the range of the integer you want to convert, you can do this directly: this is a little example to convert from float to int assuming that the float is >=0 && < 256:


unsigned char flt_to_byte (float a)

{

  float x = a + 256.0f;

  return ((*(int*)&x)&0x7fffff)>>15;

}


My music: http://myspace.com/planetarchh <-- my music

My stuff: torus.untergrund.net <-- some diy electronic stuff and more.

#6 anubis

    Senior Member

  • Members
  • PipPipPipPip
  • 2225 posts

Posted 25 June 2007 - 04:00 PM

Well... I have nothing to add to that :)
If Prolog is the answer, what is the question ?

#7 Nautilus

    Senior Member

  • Members
  • PipPipPipPip
  • 358 posts

Posted 25 June 2007 - 04:49 PM

Thanks for the links, Kusma and Nils.
I'll use Google's cache to read the pages.
IE and Mozilla fail to open them directly =/

Hi Anubis,

anubis said:

If I remember correctly the cast ops are slow because they actually result in a function call to ftol.
Correct.
More precisely, with _ftol two FPU state changes happen.
First the FPU current rounding mode is changed, then the
desired bitmask is built, and finally the FPU rounding mode
is set back to the original state.
This is done to guarantee taht the ANSI C standard is fully
respected, no matter the platform, because there's no
dedicated asm instruction***.
[edit]
*** maybe due to the fact that the IEEE 754 is still under
R&D, and may change in future. Just a wild guess...
[/edit]

These state changes stall the FPU twice per each cast.
Collaterally, the designated CPU pipe has to sit and rot
while waiting for the FPU to finish.
To add insult to injury, the state changes are unnecessary
in most cases anyway.

anubis said:

Simply write a piece of assembly code that pops the float you want from the floating point stack. That will atuomatically truncate it. I can post a piece of code when I'm home from work in a few hours or so.
I didn't mention that I know little to no asm.
I recognize jumps, and some basic instructions.
I know that the count of "push" and "pop" must always
match.
But that's all.

As far as my job is concerned, I don't need to know assembly.
Yet game programming is my 2nd hobby, and I'll have to
buy a good book someday.
Examining the asm produced by the compiler isn't enough.



Kind regards everyone,
Ciao ciao : )
-Nautilus

(readin' this? you ought to get out more)


#8 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 25 June 2007 - 04:53 PM

You can do Nils' trick safely for any float up to (but not including) 16777216. If you want a full positive int, use a double
unsigned ftoi(double d)
{
    d += 4503599627370496.0;  // 1 << 52
    return (unsigned &)d;
}

C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#9 Kenneth Gorking

    Senior Member

  • Members
  • PipPipPipPip
  • 939 posts

Posted 25 June 2007 - 05:49 PM

More fun: :)

int FloatToInt(float x)

{

	unsigned e = (0x7F + 31) - ((* (unsigned*) &x & 0x7F800000) >> 23);

	unsigned m = 0x80000000 | (* (unsigned*) &x << 8);

	return int((m >> e) & -(e < 32));

}

Any reason why the values has to be truncated? Rounding is faster, and this SSE2 snippet performs quite well:

#include <intrin.h>

int FloatToInt_SSE(float x)

{

	return _mm_cvt_ss2si( _mm_load_ss(&x) );

}

"Stupid bug! You go squish now!!" - Homer Simpson

#10 t0rakka

    New Member

  • Members
  • Pip
  • 9 posts

Posted 25 June 2007 - 06:16 PM

While we're on the topic, any fast/efficient way to do:

1. float[4] -> *unsigned* int [4] conversion from SSE xmm(n) (to anywhere, xmm register would be fine ;)

2. float[4] -> unsigned char [4] (normalized) conversion from SSE xmm(n) to anywhere, xmm, mm, 32 bit ALU register, .. memory (!)

The case 1 is easy for signed, but for unsigned have to swizzle, crackle and pop the values in multiple parts through mm registers. Likewise for case 2, am I missing something vital or is "unsigned" a second class citizen with SSE? (SSE2, SSE3, ..)

// example of case 2
__m128 c = ...;
__m64 frag = _mm_cvtps_pi16©;
uint32 v = _mm_cvtsi64_si32(_mm_packs_pu16(frag,frag));

FWIW, there is a better sequence for what I actually use this for (mantissa extraction) but it's not so good with the case 1, so I came wondering while bumping into this thread.. my bad. :O

#11 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 25 June 2007 - 06:37 PM

I wonder why everyone uses the *(type*)&x notation while (type&)x works just fine. Must be old C remnants ;)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#12 Nautilus

    Senior Member

  • Members
  • PipPipPipPip
  • 358 posts

Posted 25 June 2007 - 08:17 PM

.oisyn said:

I wonder why everyone uses the *(type*)&x notation while (type&)x works just fine. Must be old C remnants :)
Ciao .oysin,
(type&)x is prettier but less secure than *(type*)&x.
Indeed both produce the same result, but consider the case:

float f = 1.0f;


// Forget the &, and the compiler may not complain.

// In this case it won't.

DWORD d = (DWORD &) f;



float f = 1.0f;


// Forget either a * or the &, and the compiler will catch

// the error.

DWORD d = *(DWORD*) &f;


Also (DWORD &) f at first glance looks like a plain cast to DWORD.
Using *(DWORD*) &f instead you hardly get fooled, even if quickly skimming through the code.

Ciao ciao : )
-Nautilus

(readin' this? you ought to get out more)


#13 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 25 June 2007 - 08:20 PM

If that's your reasoning, you should use reinterpret_cast anyway.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#14 Nautilus

    Senior Member

  • Members
  • PipPipPipPip
  • 358 posts

Posted 25 June 2007 - 08:42 PM

You kidding? reinterpret_cast<> is too long and boring to
type every time, and even uglier than *(type*)&x
I prefer the old style casts, thanks ^_^

Ciao ciao : )
-Nautilus

(readin' this? you ought to get out more)


#15 t0rakka

    New Member

  • Members
  • Pip
  • 9 posts

Posted 25 June 2007 - 08:48 PM

reinterpret_cast is great! The whole point is that when you're casting, you *should* sweat your a** off! The less you cast the better you are off (meaning: you chose your types wisely yada yada..)

But when Man Gotta Cast, Man Gotta Cast.. *_cast<> ! FAP!

And a worn-out cliche: "and you can grep _cast..."

#16 Nils Pipenbrinck

    Senior Member

  • Members
  • PipPipPipPip
  • 597 posts

Posted 25 June 2007 - 08:51 PM

.oisyn said:

I wonder why everyone uses the *(type*)&x notation while (type&)x works just fine. Must be old C remnants :)

Neither of them are secure since they break the type aliasing rule. The only secure way to do it is to put float and int into a union and access them as needed. Most compilers will sort such little float to int hacks without any overhead these days.

Btw oisyn,

Nice little code snippet! I knew there is a general purpose way to do this cast conversion, but I never had the time to do it. I always write these kind of quick-casts on purpose, hand-tailored for the special case I need (sometimes it's fixed point, sometimes not).


Anyways, I'd like to note that these casts work in reverse just as well. Construct your float from an integer, add he correct exponent and subtract some magic float from it. Once you've understood the float format it becomes second nature.

Nils
My music: http://myspace.com/planetarchh <-- my music

My stuff: torus.untergrund.net <-- some diy electronic stuff and more.

#17 Nautilus

    Senior Member

  • Members
  • PipPipPipPip
  • 358 posts

Posted 25 June 2007 - 09:15 PM

@ Nils:
You are quite right, as usual.
And I forgot my manners by not saying "thanks" to .oysin, for the snippet you just mentioned.


@ .oysin:
Thank you : )


@ Kenneth:
Thank you too.
I need truncation because I often cast to int from floats with fractional parts and the truncation of float() is handy.
Rounding has its uses too, of course.
I think I now have enough material to go on.
But if any of you has more to share, feel free to do so!


@ t0rakka:
I've read an article, once, where the author was complaining about the triad of the _cast<> operators.
He said that they are meant to substitute the old C style casts.
C++ programmers should always resort to such _cast<> and forget about the old C way.
On the other hand -and here I'll repeat part of what you said- casts are evil and should be avoided.
And that's why when they had to decide on syntax and names of the new C++ _cast<> operators, they chose such long and inelegant names. Kind of to discourage their use.

The article concluded saying that the _cast<> ops were superfluous and were adding nothing -in terms of functionality- to the existing language.
I don't remember the link anymore, sorry.

IIRC the article's title had the words "Why I hate [+something else]" in it.
It was one of several articles on C++.
The site is still up for sure.


Regards,
Ciao ciao : )
-Nautilus

(readin' this? you ought to get out more)


#18 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 25 June 2007 - 11:01 PM

Nautilus said:

You kidding? reinterpret_cast<> is too long and boring to

Look, either you want to be safe from mistakes, or you wat to type something short. If you want to be safe, you should use the C++ style casts (with reinterpret_cast you express what you mean and you can't accidentally cast away cv qualifiers - A C style cast could do pointer conversions if the types are related) :)

Quote

I prefer the old style casts, thanks ;)
Which is perfectly fine, as long as you keep in mind they're not that safe as C++ style casts.

Nils Pipenbrinck said:

Neither of them are secure since they break the type aliasing rule. The only secure way to do it is to put float and int into a union and access them as needed. Most compilers will sort such little float to int hacks without any overhead these days.
What kind of security are you talking about exactly? I fail to see the difference between a union and reinterpret_cast, since from the standard's point of view, you're only allowed to read the member from a union you have previously written (so technically writing to the float in the union and then reading the int yields undefined behaviour, just as with reading an int from a float reference - the compiler can make the same set of assumptions in both cases)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#19 EDWYN

    New Member

  • Members
  • Pip
  • 1 posts

Posted 22 March 2010 - 05:49 AM

Nils Pipenbrinck said:

This one might be interesting for you as well:

http://www.stereopsis.com/FPU.html

yes, its really very much interesting and very informative piece of work.
Everything is Possible ;)

#20 GloW

    New Member

  • Members
  • Pip
  • 1 posts

Posted 05 April 2012 - 03:17 PM

Hello

i've suceeded into using Kenneth Godring code, but i've not fully understood how this work.

I try to modify the code of FloatToInt into DoubleToUnsignedShort, if someone can give me a Hand.

int FloatToInt(float x)
{
	    unsigned e = (0x7F + 31) - ((* (unsigned*) &x & 0x7F800000) >> 23);
	    unsigned m = 0x80000000 | (* (unsigned*) &x << 8);
	    return int((m >> e) & -(e < 32));
}

Anyway i would like to understand :
- The first line extract the exponent, all right but why 0x7f + 31 at the beginning?
- The second line extract the mantissa , all rigth but why addinf a forced to 1 MSB?
- The third line confuse me

Thanks for Help





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users