I ran into this strange issue when trying to generate some random 64-bit numbers and noticed that this bit of code would only give me numbers where the lower 32-bits were < 0x8000_0000.
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <cstdlib>
#include <ctime>
using namespace std;
void bin_range(uint64_t addr);
void print_range();
uint64_t GiB = 1024*1024*1024;
#define NUM_TRIES 10000
int range[14] = {0};
int main(){
srand(time(NULL));
for(int i = 0; i < NUM_TRIES; ++i){
uint64_t addr = ((uint64_t) rand() << 32) | rand();
addr &= 0x3FFFFFFFFFull;
if(addr > ((uint64_t) 14*GiB)){
--i;
continue;
}
bin_range(addr);
}
print_range();
return 0;
}
void bin_range(uint64_t addr){
if(addr < GiB)
range[0]++;
else if(addr < 2*GiB)
range[1]++;
else if(addr < 3*GiB)
range[2]++;
else if(addr < 4*GiB)
range[3]++;
else if(addr < 5*GiB)
range[4]++;
else if(addr < 6*GiB)
range[5]++;
else if(addr < 7*GiB)
range[6]++;
else if(addr < 8*GiB)
range[7]++;
else if(addr < 9*GiB)
range[8]++;
else if(addr < 10*GiB)
range[9]++;
else if(addr < 11*GiB)
range[10]++;
else if(addr < 12*GiB)
range[11]++;
else if(addr < 13*GiB)
range[12]++;
else if(addr < 14*GiB)
range[13]++;
}
void print_range(){
for(int i = 0; i < 14; ++i){
cout <<dec<< "range["<<i<<"]\thas "<<((float)range[i]/NUM_TRIES)*100.0<<"%"<<endl;
}
}
When I run this I see that only the address ranges with addr[31:28] < 8 make it through this if statement!
range[0] has 12.25%
range[1] has 12.17%
range[2] has 0%
range[3] has 0%
range[4] has 13.22%
range[5] has 12.19%
range[6] has 0%
range[7] has 0%
range[8] has 12.53%
range[9] has 11.83%
range[10] has 0%
range[11] has 0%
range[12] has 12.85%
range[13] has 12.96%
Here's the relevant disassembly for this if statement when compiling with g++ -g -c -fverbose-asm -Wa,-adhln calc.cpp > calc.lst and I'm using g++ version 4.4.6:
32:calc.cpp **** addr &= 0x3FFFFFFFFFull;
407 .loc 2 32 0
408 00ad 48B8FFFF movabsq $274877906943, %rax #, tmp129
408 FFFF3F00
408 0000
409 00b7 482145E0 andq %rax, -32(%rbp) # tmp129, addr
33:calc.cpp **** if(addr > ((uint64_t) 14*GiB)){
410 .loc 2 33 0
411 00bb 488B0500 movq GiB(%rip), %rax # GiB, GiB.59
411 000000
412 00c2 4801C0 addq %rax, %rax # tmp131
413 00c5 488D14C5 leaq 0(,%rax,8), %rdx #, tmp132
413 00000000
414 00cd 4889D1 movq %rdx, %rcx # tmp132,
415 00d0 4829C1 subq %rax, %rcx # tmp130,
416 00d3 4889C8 movq %rcx, %rax #, D.22316
417 00d6 483B45E0 cmpq -32(%rbp), %rax # addr, D.22316
418 00da 7306 jae .L20 #,
34:calc.cpp **** --i;
It seems to me that g++ is making the if statement a 32-bit comparison, but from the disassembly I see it's a quad-word compare so it doesn't make sense to me why this is happening. Any ideas?
Update: I know this isn't an issue with rand() because when I change the for loop to be like this:
for(int i = 0; i < NUM_TRIES; ++i){
uint64_t addr = ((uint64_t) rand() << 32) | rand();
addr %= 14*GiB;
//addr &= 0x3FFFFFFFFFull;
//if(addr > ((uint64_t) 14*GiB)){
//--i;
//continue;
//}
bin_range(addr);
}
I do get a full range of numbers for the output:
range[0] has 7.09%
range[1] has 6.85%
range[2] has 7.24%
range[3] has 7.75%
range[4] has 7.39%
range[5] has 7.19%
range[6] has 6.92%
range[7] has 6.63%
range[8] has 7.33%
range[9] has 6.95%
range[10] has 7.11%
range[11] has 7.28%
range[12] has 7.08%
range[13] has 7.19%
Aucun commentaire:
Enregistrer un commentaire