5

I am using glm::linearRand(-1.0f, 1.0f) to generate random floating point numbers between -1 and 1. Afterwards, I output the percentage of numbers that are positive (0.0f or above).

std::srand(time(0)); // Give glm a new seed

uint32_t samples = 1000000000;
uint32_t positive = 0;
uint32_t negative = 0;

for (uint32_t i = 0; i < samples; i++) {
    float rand = glm::linearRand(-1.0f, 1.0f);

    if (rand >= 0.0f) {
        positive++;
    } else {
        negative++;
    }
}

std::cout << "positive %: " << std::setprecision(6) << ((float)positive / samples) * 100 << std::endl;

The percentage of positive numbers always ends up around 49.6%, no matter how often I run the program (with different seeds!). If I understand floating point numbers correctly, there are equally many between -1.0f and 0.0f as there are between 0.0f and 1.0f.

So why does this program always generate more negative numbers than positive numbers?

Note this test code provided by Bob__ (result of comments exchange between him, IkarusDeveloper and Marek R). This proves that rand is not problem this time, but there is some problem with floats rounding inside of glm::linearRand.

Marek R
  • 32,568
  • 6
  • 55
  • 140
Naemesis
  • 139
  • 5
  • which system/compiler? If MSVC then MSVC has very old `rand` which RAND_MAX has only 16 bits. – Marek R Feb 18 '22 at 12:10
  • Unrelated, just to let you know that there is also a prefix increment operator – MatG Feb 18 '22 at 12:17
  • Is it always the exactly the same positive value (if you don't round it)? Does positive + negative = 1000000000. What if you decrease/increase from 1000000000? – Andrew Feb 18 '22 at 12:26
  • [Live demo](https://godbolt.org/z/YqdYKc3o9) – Marek R Feb 18 '22 at 12:28
  • It is not a duplicate, so I voted to reopen. This is more like `rand` has poor quality. – Marek R Feb 18 '22 at 12:34
  • [This is best](https://stackoverflow.com/a/52870307/1387438) I've found so far about `rand`, but it doesn't explain why there are more negative numbers in this case. – Marek R Feb 18 '22 at 12:39
  • https://onlinegdb.com/FsCaJhXjP0 it looks like it depends on the implementation of `std::rand`, in the sample on onlinegdb it push out 50.0008% positive numbers, you may copy my sample and check if your computer push out the same numbers that you found with your sample. – IkarusDeveloper Feb 18 '22 at 12:44
  • @IkarusDeveloper my live demo uses `glm` library so it is exact same as OP code. Your example uses `rand()` in a bit different way. In fact you are using `double` in many places, so different rounding issue pops up. This is a good clue what actual problem is. – Marek R Feb 18 '22 at 12:50
  • @MarekR I changed the code made sure to only use floats and the result remains different. My example does not use glm but uses its own implementation, I don't think that's what makes the difference. I think it is more likely that the difference is caused by different implementations of `std::rand` in the C-runtime library. https://onlinegdb.com/hGCxCFFe7 – IkarusDeveloper Feb 18 '22 at 13:49
  • 1
    so i tested on your online compiler and the result is really strange. https://godbolt.org/z/1f1cT8z7E i just copied glm::getLinearRand implementation on testRand and it push out differently – IkarusDeveloper Feb 18 '22 at 14:03
  • 2
    I voted for reopen, because the culprit [doesn't seem](https://godbolt.org/z/9jq1bG35j) to be `rand`, this time, but `glm::linearRand`. – Bob__ Feb 18 '22 at 15:19

1 Answers1

4

This is a bug in GLM. While the usual admonition about using % with rand is that the range doesn’t evenly divide RAND_MAX, this code opts for the more straightforward approach of reducing rand() modulo UINT8_MAX, so that 255 is never produced. Every random value is ultimately derived from combining several such bytes, so 127/255=49.8% of the values will be in the upper half (here, positive).

Davis Herring
  • 36,443
  • 4
  • 48
  • 76