www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - D equivalent of C++ reinterpret cast?

reply Bradley Mitchell <abstractant1 gmail.com> writes:
Hello,

I'm trying to implement the Quake 3 fast inverse square root algorithm which
requires casting from int to float without modifying the stored bits. A C++
reinterpret cast seems to accomplish this just fine but so far I have had no
success in D after trying quite a few different things. Here is the C++ I've
written that currently works:

#include <iostream>

float fastInvSqrt( float x )
{
    const int INV_SQRT_N = 1597292357;
    const float MULT = 1.000363245811462f;

    float const mx = 0.5f * MULT * x;
    int xi = *reinterpret_cast<int *>( &x );
    xi = INV_SQRT_N - (xi >> 1);
    x = *reinterpret_cast<float *>( &xi );
    return x * (1.5f * MULT - mx * x * x);
}

int main()
{
    float a = fastInvSqrt( 9.0f );
    std::cout << a << std::endl;
}

And here is my D code that doesn't work yet:

module test;

import std.stdio;

float fastInvSqrt( float x )
{
    const int INV_SQRT_N = 1597292357;
    const float MULT = 1.000363245811462f;

    float mx = 0.5f * MULT * x;
    int xi = cast(int)cast(void*)x;
    xi = INV_SQRT_N - (xi >> 1);
    x = xi;
    x = cast(float)cast(void*)xi;
    return x * (1.5f * MULT - mx * x * x);
}

void main(string[] args)
{
    float a = fastInvSqrt( 9.0f );
    writefln("%f", a);
}

For further reference, the wikipedia article on this algorithm can be found
here:
http://en.wikipedia.org/wiki/Fast_inverse_square_root

and my C++ code is based on a slightly modified version described here:
http://www.ece.uwaterloo.ca/~dwharder/Algorithms_and_Data_Structures/Algorithms/Inverse_square_root/

As you can see, I'm not trying to convert an integer to an equivalent floating
point value but rather trying to coerce the variable to a float without
modifying the underlying bit sequence. Any help would be greatly appreciated!!

Thank you for your time,
Brad
Sep 19 2010
next sibling parent reply orgoton baberek <sdf sa.com> writes:
On 19/09/2010 18:39, Bradley Mitchell wrote:
 Hello,

 I'm trying to implement the Quake 3 fast inverse square root algorithm which
 requires casting from int to float without modifying the stored bits. A C++
 reinterpret cast seems to accomplish this just fine but so far I have had no
 success in D after trying quite a few different things. Here is the C++ I've
 written that currently works:

 #include<iostream>

 float fastInvSqrt( float x )
 {
      const int INV_SQRT_N = 1597292357;
      const float MULT = 1.000363245811462f;

      float const mx = 0.5f * MULT * x;
      int xi = *reinterpret_cast<int *>(&x );
      xi = INV_SQRT_N - (xi>>  1);
      x = *reinterpret_cast<float *>(&xi );
      return x * (1.5f * MULT - mx * x * x);
 }

 int main()
 {
      float a = fastInvSqrt( 9.0f );
      std::cout<<  a<<  std::endl;
 }

 And here is my D code that doesn't work yet:

 module test;

 import std.stdio;

 float fastInvSqrt( float x )
 {
      const int INV_SQRT_N = 1597292357;
      const float MULT = 1.000363245811462f;

      float mx = 0.5f * MULT * x;
      int xi = cast(int)cast(void*)x;
      xi = INV_SQRT_N - (xi>>  1);
      x = xi;
      x = cast(float)cast(void*)xi;
      return x * (1.5f * MULT - mx * x * x);
 }

 void main(string[] args)
 {
      float a = fastInvSqrt( 9.0f );
      writefln("%f", a);
 }

 For further reference, the wikipedia article on this algorithm can be found
here:
 http://en.wikipedia.org/wiki/Fast_inverse_square_root

 and my C++ code is based on a slightly modified version described here:
 http://www.ece.uwaterloo.ca/~dwharder/Algorithms_and_Data_Structures/Algorithms/Inverse_square_root/

 As you can see, I'm not trying to convert an integer to an equivalent floating
 point value but rather trying to coerce the variable to a float without
 modifying the underlying bit sequence. Any help would be greatly appreciated!!

 Thank you for your time,
 Brad

Use a union, which is the correct and safe way. union convert { float f; uint i; } convert.f = 1.0f; writeln(convert.i); will print 1065353216 (0x3f800000)
Sep 19 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
orgoton baberek:
 Use a union, which is the correct and safe way.

C standard say that's not safe. You can force that to be safe in C-GCC if you explicitly disable a compiler optimization. I think D docs don't say anything about this. And Walter has said that regarding such things D acts as C. So I am not sure that will be safe in future D. Bye, bearophile
Sep 19 2010
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Jonathan M Davis wrote:
 On Sunday 19 September 2010 17:07:38 bearophile wrote:
 orgoton baberek:
 Use a union, which is the correct and safe way.



 you explicitly disable a compiler optimization. I think D docs don't say
 anything about this. And Walter has said that regarding such things 


 as C. So I am not sure that will be safe in future D.

 Bye,
 bearophile

I'd have to go digging in old posts, but I'm pretty sure that that's

 how you're supposed to do it in D. I haven't ever done it because I 

 that sort of casting is pretty evil and only should be used as a last 

 but I believe that unions are the correct way to handle it in D.

I remember being a part of a discussion where D'is unions were told to be exactly the same as C's. That means, results of using a union is only specified when it's used through one its members. Writing to one member and reading from another is unspecified. It is clear that endianness and padding should complicate matters. But it's still used in C in low level code. That must be because programs are not as portable as thought; and if they are, compile time checks and macro magic are possible to obtain expected behavior. Ali
Sep 19 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Ali Çehreli:
 I remember being a part of a discussion where D'is unions were told to 
 be exactly the same as C's. That means, results of using a union is only 
 specified when it's used through one its members. Writing to one member 
 and reading from another is unspecified. It is clear that endianness and 
 padding should complicate matters.

I have just written a post about this topic: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=117675 Bye and thank you, bearophile
Sep 20 2010
prev sibling next sibling parent reply Juanjo Alvarez <fake fakeemail.com> writes:
On Sun, 19 Sep 2010 20:07:38 -0400, bearophile 
<bearophileHUGS lycos.com> wrote:
 C standard say that's not safe. You can force that to be safe in 

docs don't say anything about this. And Walter has said that regarding such things D acts as C. So I am not sure that will be safe in future D. Unions are not allowed in SafeD, so I guess they are considered unsafe in D.
Sep 19 2010
parent Kagamin <spam here.lot> writes:
Juanjo Alvarez Wrote:

 Unions are not allowed in SafeD, so I guess they are considered 
 unsafe in D.

They're safer in this particular case. Note how original poster forgot about taking and address.
Sep 20 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 19 September 2010 18:19:25 Juanjo Alvarez wrote:
 On Sun, 19 Sep 2010 20:07:38 -0400, bearophile
 
 <bearophileHUGS lycos.com> wrote:
 C standard say that's not safe. You can force that to be safe in

C-GCC if you explicitly disable a compiler optimization. I think D docs don't say anything about this. And Walter has said that regarding such things D acts as C. So I am not sure that will be safe in future D. Unions are not allowed in SafeD, so I guess they are considered unsafe in D.

Well, it's not like reintepret_cast is the safest thing either. - Jonathan M Davis
Sep 19 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 19 September 2010 17:07:38 bearophile wrote:
 orgoton baberek:
 Use a union, which is the correct and safe way.

C standard say that's not safe. You can force that to be safe in C-GCC if you explicitly disable a compiler optimization. I think D docs don't say anything about this. And Walter has said that regarding such things D acts as C. So I am not sure that will be safe in future D. Bye, bearophile

I'd have to go digging in old posts, but I'm pretty sure that that's essentially how you're supposed to do it in D. I haven't ever done it because I think that that sort of casting is pretty evil and only should be used as a last resort, but I believe that unions are the correct way to handle it in D. It's been discussed before, I'm fairly certain, though I think that the last place that I saw it was buried in a topic on a specific problem rather than anything necessarily particularly searchable in the archives. - Jonathan M Davis
Sep 19 2010
prev sibling parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Bradley Mitchell <abstractant1 gmail.com> wrote:

     int xi = cast(int)cast(void*)x;

     x = cast(float)cast(void*)xi;

The simple solution (seeing as how x is an lvalue) is int xi = *cast(int*)&x; [...] x = *cast(float*)&xi; Function version: T reinterpret( T, U )( U value ) { return *cast( T* )&value; } There may be situations in which this will not work (though I know of none, OTOH), and where using a union will: T reinterpret( T, U )( U value ) { union Uni { U u; T t; } return Uni(value).t; } Of course, these are general solutions, and the easy solution to your problem is to use a union directly: float fastInvSqrt( float x ) { enum int INV_SQRT_N = 1597292357; enum float MULT = 1.000363245811462f; float mx = 0.5f * MULT * x; union Xu { float f; int i; } Xu xu = Xu(x); xu.i = INV_SQRT_N - (xu.i >> 1); return xu.f * (1.5f * MULT - mx * xu.f * xu.f); } -- Simen
Sep 19 2010
parent Bradley Mitchell <abstractant1 gmail.com> writes:
Yes, that did it exactly!

Thank you, Simen, and Orgoton.
Sep 19 2010