www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - strange math

reply Matt Watkins <Matt_member pathlink.com> writes:
I am probably doing something dumb here, but I can't figure it out, so I am
wondering if someone here can help. I am trying to generate a random number
between 900 million and 1.1 billion, and store it in a float. Here are the two
code segments in question:

float div1;
//Code segment 1
for(int x = 0; x < 10; x++)
{
div1 = 1000000000.0f + cast(float)((rand() % 100000000) * ((rand() % 2) * 2 -
1));
writef("%f\n", div1);
}

//Code segment 2
for(int x = 0; x < 10; x++)
{
int addval = (rand() % 100000000) * ((rand() % 2) * 2 - 1);
div1 = 1000000000.0f + cast(float)(addval);
writef("%f\n", div1);
}

These two segments to me look like they should do the same thing. The only
difference between them is that the second segment uses a variable addval to do
(rand() % 100000000) * ((rand() % 2) * 2 - 1), where the first one just has the
code stuck right in the one line. Here is the output:
Code segment 1
1083211520.000000
5233628160.000000
5259153408.000000
1010581632.000000
5244919808.000000
1038838528.000000
1060295488.000000
5250748928.000000
5242580992.000000
1017323776.000000
Code segment 2
987565312.000000
901736640.000000
1040207936.000000
1039182272.000000
978791104.000000
1031670656.000000
1070735040.000000
997212864.000000
1054348672.000000
1069398592.000000
As you can see, the second segment behaves as expected, but the first segment
generates random numbers around 1 billion or around 5 billion. Can someone
please tell me why the first segment behaves the way it does? Thanks.

Also, I think I have the lastest version of the dmd compiler, since I updated no
more than 2 weeks ago.
Nov 23 2005
parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Hi,

In article <dm2ar6$1h0f$1 digitaldaemon.com>, Matt Watkins says...
I am probably doing something dumb here, but I can't figure it out, so I am
wondering if someone here can help. I am trying to generate a random number
between 900 million and 1.1 billion, and store it in a float. Here are the two
code segments in question:

float div1;
//Code segment 1
for(int x = 0; x < 10; x++)
{
div1 = 1000000000.0f + cast(float)((rand() % 100000000) * ((rand() % 2) * 2 -
1));
writef("%f\n", div1);
}

//Code segment 2
for(int x = 0; x < 10; x++)
{
int addval = (rand() % 100000000) * ((rand() % 2) * 2 - 1);
div1 = 1000000000.0f + cast(float)(addval);
writef("%f\n", div1);
}

These two segments to me look like they should do the same thing. The only
difference between them is that the second segment uses a variable addval to do
(rand() % 100000000) * ((rand() % 2) * 2 - 1), where the first one just has the
code stuck right in the one line. Here is the output:

[snip]
As you can see, the second segment behaves as expected, but the first segment
generates random numbers around 1 billion or around 5 billion. Can someone
please tell me why the first segment behaves the way it does? Thanks.

Welcome to the wonderful world of integral promotion. :) I've been bitten by exactly this before... (see http://www.digitalmars.com/d/archives/digitalmars/D/28116.html) Here is a breakdown of why this happens: (I hope it is possible to follow) rand() returns uint uint % int gets promoted to uint uint * int gets promoted to uint uint - int gets promoted to uint ((rand() % 2) * 2 - 1) therefore gets promoted to uint... This give two possible numbers: 1u or 4294967295u (rand() % 100000000) also gets promoted to uint two uints get multiplied giving a new uint (will overflow in 50% of the cases) Assume (rand() % 100000000) evaluates to 99999999u... the wounderful nature of 2-compliment math gives: cast(int) 4294967295u * 99999999u == -99999999 Therefore, everything works in the second case. in the first case, you have instead: cast(float) 4294967295u * 99999999u == 4.19497e+09 (approximately) And everything blows up. The cleanest fix to your code may be: #float div1; #//Code segment 1 #for(int x = 0; x < 10; x++) #{ # div1 = 1000000000.0f + cast(float)((rand() % 100000000) * cast(int)((rand() % 2) * 2 - 1)); # writef("%f\n", div1); #} Note: You will get exactly the same results in C. (Not that I've tested...) D has the same integral promotion rules as C has. Regards Oskar
Nov 23 2005