• srand(0)

    From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Mon Dec 22 08:48:10 2025
    From Newsgroup: comp.lang.c

    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Mon Dec 22 06:44:42 2025
    From Newsgroup: comp.lang.c

    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Mon Dec 22 13:18:19 2025
    From Newsgroup: comp.lang.c

    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Mon Dec 22 12:13:37 2025
    From Newsgroup: comp.lang.c

    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if seeded
    with 0. ...

    The details of how the seed affects the random number sequence are
    unspecified by the standard. I personally would consider a pseudo-random
    number generator to be quite defective if there were any seed that
    produced a constant output.

    ... And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    The C standard explains that as follows: "If rand is called before any
    calls to srand have been made, the same sequence shall be generated as
    when srand is first called with a seed value of 1." (7.24.2.2p2).

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Mon Dec 22 18:41:10 2025
    From Newsgroup: comp.lang.c

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if seeded
    with 0. ...

    The details of how the seed affects the random number sequence are unspecified by the standard. I personally would consider a pseudo-random number generator to be quite defective if there were any seed that
    produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With sophisticated
    generator polynomials they produce also sequences of maximum period;
    say, for N=31 a non-repeating sequence of length 2^N - 1. The one
    element that is missing from the sequence is the 0 (that reproduces
    itself).

    Technically you pick some bit-values from fixed positions (depending
    on the generator polynomial) of the register and xor the bits to shift
    the result into the register. Here's ad hoc an example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >> 30))
    & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Mon Dec 22 20:45:38 2025
    From Newsgroup: comp.lang.c

    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence are unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there were
    any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With sophisticated generator polynomials they produce also sequences of maximum period;
    say, for N=31 a non-repeating sequence of length 2^N - 1. The one
    element that is missing from the sequence is the 0 (that reproduces
    itself).

    Technically you pick some bit-values from fixed positions (depending
    on the generator polynomial) of the register and xor the bits to shift
    the result into the register. Here's ad hoc an example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to always produces the same sequence. There is no requirement that different
    seeds have to produce different sequences.
    So, for generator in your example, implementation like below would be
    fully legal. Personally, I wouldn't even consider it as particularly
    poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea for different reason: LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The process
    can be accelerate by generation of several bits at time via look up
    tables, but in order to get decent speed the table has to be rater big
    and using big tables in standard library is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential
    Generators, probably because for Stanadard library compactness of both
    code and data is considered more important than very high speed (not
    that on modern HW LCGs are slow) or superior random properties of
    Mersenne Twisters.
    [/O.T]

    IMHO, decent PRNG with decent thread-safe interface (not POSIX
    imbecile rand_r() ) should have been part of the C Standard
    library at least since C11. But somehow it did not happen until now.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@046-301-5902@kylheku.com to comp.lang.c on Mon Dec 22 19:16:06 2025
    From Newsgroup: comp.lang.c

    On 2025-12-22, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if seeded
    with 0. ...

    The details of how the seed affects the random number sequence are
    unspecified by the standard. I personally would consider a pseudo-random
    number generator to be quite defective if there were any seed that
    produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers).

    The entire class of LFRS's is not homogeneous in this way; it
    is possible for a Linear Boolean function to generate a nonzero
    out of zero inputs.

    The LFSR page in the Wikipedia references this definition:

    https://en.wikipedia.org/wiki/Linearity#Boolean_functions

    A Boolean linear function of the inputs x1, ... xn is
    a kind of polynomial with coefficients C0, C1, ... CN combined
    with the inputs using AND for multiplication, and XOR for addition:

    f(x1, ... xn) = C0 ^ (C1 & x1) ^ (C2 & x2) ^ .. ^ (Cn ^ xn)

    If c0 is chosen as 1, then we get a 1 output when all the x's are
    zero. (The article uses "b" for the parameters and "a" for the
    coefficients, which I'm not crazy about.)

    Also see the two conditions, given in the description, one of which has
    to be true for Boolean linearity. For condition (2) the remark is given
    that under that condition f(F, F, F, ... F) = T.

    So if we have a "condition 2" linear function, our LSFR avoids
    the behavior.

    I don't recall having paid attention to this exact material in the
    past so it is a "TIL" for me.

    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >> 30))
    & 0x1;

    These could of course be XOR, without it making a difference; the least significant bit in a binary addition is the XOR of the LSB's of the
    inputs.

    reg <<= 1;
    reg |= new_bit;

    So to have that C0 = 1 coefficient in the linear function, we just make
    it "reg |= (new_bit ^ 1);".
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Mon Dec 22 21:16:53 2025
    From Newsgroup: comp.lang.c

    On Mon, 22 Dec 2025 20:45:38 +0200, Michael S wrote:

    ... (not POSIX imbecile rand_r() ) ...

    Obsoleted in newer POSIX, I see.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Mon Dec 22 22:19:56 2025
    From Newsgroup: comp.lang.c

    On 2025-12-22 19:45, Michael S wrote:
    [...]
    In practice, using LFSR for rand() is not particularly bright idea for different reason: LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. [...]

    Please note that I've not suggested that.

    I had been merely answering your question concerning Michael's doubts...

    Is it incorrect to use 0 (zero) to seed srand()?
    No, why whould you think so?
    There's number sequence generators that produce 0 sequences if seeded
    with 0.

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Mon Dec 22 22:35:35 2025
    From Newsgroup: comp.lang.c

    On 2025-12-22 20:16, Kaz Kylheku wrote:
    On 2025-12-22, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    [...]

    I don't recall having paid attention to this exact material in the
    past so it is a "TIL" for me.

    What does "TIL" mean?

    I actually had used PN-generators in the past; used it in a Pascal
    program to make statistical tests, and also in assembler for a DSP
    codec to create reproducible random payload data to compare after
    transmission. For that purpose it was the perfect choice.


    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >> 30))
    & 0x1;

    These could of course be XOR, without it making a difference; the least significant bit in a binary addition is the XOR of the LSB's of the
    inputs.

    Actually the pluses are a remnant of my decades old piece of Pascal
    code that I wrote back then that I now just transcribed. The Pascal
    version I used back then didn't support XOR so I used the addition.
    Being in a boolean context I'd nowadays (in C) have rather written
    purely new_bit = ((reg>>2)^(reg>>4)^(reg>>6)^(reg>>30)) & 0x1;
    but since it's unimportant, as you say, I lazily left it as it was.

    Janis

    [...]

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Mon Dec 22 22:57:27 2025
    From Newsgroup: comp.lang.c

    On 2025-12-22 19:45, Michael S wrote:
    [...] LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The process
    can be accelerate by generation of several bits at time via look up
    tables, but in order to get decent speed the table has to be rater big
    and using big tables in standard library is bad sportsmanship.

    Yes. But mind that the speed is also depending on what quality you
    need. For example; I used the PN-generator to create bit-sequences
    (as you also suggest). For another application both, PN-LFSR and
    LCG (that you mention below), were inacceptable; we used a cipher
    to create the random data. (If you compare the speed of creating
    the cipher to a bit-shift-register the latter looks really fast.)


    It seems that overwhelming majority C RTLs use Linear Congruential Generators, probably because for Stanadard library compactness of both
    code and data is considered more important than very high speed (not
    that on modern HW LCGs are slow) or superior random properties of
    Mersenne Twisters.

    For "standard applications" I always used the simple LCGs; simple
    and fast. Or whatever the tools or library provided; which were
    mostly anyway LCGs.

    Janis

    [...]

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From John McCue@jmclnx@gmail.com.invalid to comp.lang.c on Tue Dec 23 00:39:49 2025
    From Newsgroup: comp.lang.c

    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));
    --
    [t]csh(1) - "An elegant shell, for a more... civilized age."
    - Paraphrasing Star Wars
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Tue Dec 23 02:17:01 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random number. Seem
    easier and more portable across Linux & the *BSDs.

    Not to mention a lot stronger, cryptographically.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Ike Naar@ike@sdf.org to comp.lang.c on Tue Dec 23 06:49:22 2025
    From Newsgroup: comp.lang.c

    On 2025-12-23, John McCue <jmclnx@gmail.com.invalid> wrote:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    srand takes an unsigned argument.

    unsigned s;
    read(fd, &s, sizeof s);
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Tue Dec 23 07:17:05 2025
    From Newsgroup: comp.lang.c

    On Mon, 22 Dec 2025 06:44:42 -0500, James Kuyper wrote:

    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    Excuse my delayed reply James (net provider was down most of today).

    Well, I guess I did not expect such large differences between
    gcc & musl somehow (cant test with clang just yet). I understand the
    sequence is deterministic & likely still some differences with musl,
    yet I wrongly assumed it seems, the sequences would be the same...

    #include <stdio.h>
    #include <stdlib.h>

    int main(void) {
    srand(0);
    printf("%d\n", rand());
    return 0;
    }

    /*

    $ gcc -O2 -o rnd rnd.c && ./rnd
    1804289383

    $ musl-gcc -static -O2 -o rnd rnd.c && ./rnd
    2049033599

    */
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Tue Dec 23 07:24:34 2025
    From Newsgroup: comp.lang.c

    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    I see no difference when invoking any of:

    man 3 srand
    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Tue Dec 23 07:25:42 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue Dec 23 08:25:59 2025
    From Newsgroup: comp.lang.c

    On 23/12/2025 08:17, Michael Sanders wrote:
    On Mon, 22 Dec 2025 06:44:42 -0500, James Kuyper wrote:

    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    Excuse my delayed reply James (net provider was down most of today).

    Well, I guess I did not expect such large differences between
    gcc & musl somehow (cant test with clang just yet). I understand the
    sequence is deterministic & likely still some differences with musl,
    yet I wrongly assumed it seems, the sequences would be the same...

    #include <stdio.h>
    #include <stdlib.h>

    int main(void) {
    srand(0);
    printf("%d\n", rand());
    return 0;
    }

    /*

    $ gcc -O2 -o rnd rnd.c && ./rnd
    1804289383

    $ musl-gcc -static -O2 -o rnd rnd.c && ./rnd
    2049033599

    */


    It is not the compilers that are different, it is the C standard
    libraries that are different. gcc is a compiler, not a library, but you
    are probably using glibc with it by default. musl is a library, not a compiler. There is no reason to suppose that different C standard
    libraries use the same implementation of rand()and srand(), so no reason
    to suppose they would give the same sequences - though each on their own
    will give a deterministic pseudo-random sequence based on their seeds.

    If you swap gcc with clang you will get the same results - it will
    depend on whether you are linking with glibc, musl, or another C library.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue Dec 23 09:59:27 2025
    From Newsgroup: comp.lang.c

    On 2025-12-23 08:24, Michael Sanders wrote:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    Sorry, can't help you here. Maybe someone else can.

    Myself I only access the Unix man pages as they come,
    i.e. using either 'man entry' or 'man section entry'.
    The POSIX information is usually textually integrated
    in the man pages.


    I see no difference when invoking any of:

    man 3 srand

    That's what I'm doing, and I see, for example,

    ...
    HISTORY
    rand()
    srand()
    SVr4, 4.3BSD, C89, POSIX.1-2001.

    rand_r()
    POSIX.1-2001. Obsolete in POSIX.1-2008.
    ...

    Janis

    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Tue Dec 23 11:18:55 2025
    From Newsgroup: comp.lang.c

    On Mon, 22 Dec 2025 22:57:27 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 19:45, Michael S wrote:
    [...] LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The
    process can be accelerate by generation of several bits at time via
    look up tables, but in order to get decent speed the table has to
    be rater big and using big tables in standard library is bad
    sportsmanship.

    Yes. But mind that the speed is also depending on what quality you
    need. For example; I used the PN-generator to create bit-sequences
    (as you also suggest). For another application both, PN-LFSR and
    LCG (that you mention below), were inacceptable; we used a cipher
    to create the random data. (If you compare the speed of creating
    the cipher to a bit-shift-register the latter looks really fast.)


    It seems that overwhelming majority C RTLs use Linear Congruential Generators, probably because for Stanadard library compactness of
    both code and data is considered more important than very high
    speed (not that on modern HW LCGs are slow) or superior random
    properties of Mersenne Twisters.

    For "standard applications" I always used the simple LCGs; simple
    and fast. Or whatever the tools or library provided; which were
    mostly anyway LCGs.

    Janis

    [...]


    When I need PRNG then I am typically not deeply concerned about size of
    its internal state. On the other hand, I don't want to care about
    potentially insufficient randomness of the output (not in crypto
    sense). On the 3rd hand, vectors that I generate with PRNG tend to be
    big and I don't like to wait, so I do care somewhat about speed.
    Those 3 factors together plus availability long ago made MT19937-64
    into my personal default PRNG of choice.

    MT19937-64 is available out of the box(*) in C++. But not in C,
    unfortunately.

    At higher theoretical level MT is a generalization of LFSR, but it is
    not obvious when one looks at implementation.

    ---------
    * - hidden behind unnecessary levels of abstraction that just make it
    harder to use, but that's another story.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Tue Dec 23 10:54:23 2025
    From Newsgroup: comp.lang.c

    On 2025-12-23 10:18, Michael S wrote:

    When I need PRNG then I am typically not deeply concerned about size of
    its internal state. On the other hand, I don't want to care about
    potentially insufficient randomness of the output (not in crypto
    sense). On the 3rd hand, vectors that I generate with PRNG tend to be
    big and I don't like to wait, so I do care somewhat about speed.
    Those 3 factors together plus availability long ago made MT19937-64
    into my personal default PRNG of choice.

    I've never intensified my knowledge in direction of MT algorithms.


    MT19937-64 is available out of the box(*) in C++. But not in C, unfortunately.

    This is really strange given that the name ("Mersenne Twister") is
    that prominent.

    Looking that up I find at least "C" code for MT19937 in Wikipedia https://de.wikipedia.org/wiki/Mersenne-Twister
    It's based on 32 bit logic it seems; interpreting your "MT19937-64"
    I assume you're looking for a 64 bit based version?


    At higher theoretical level MT is a generalization of LFSR, but it is
    not obvious when one looks at implementation.

    Well, at least there's the 'mod' operations all based on powers of 2
    along with all the binary op's which suggests some (non-arithmetic) bit-register type of algorithm, but the multiplication with 0x9908b0df
    (5 * 513496723) - which I'd suppose be hard to realize as/with LFSR -
    may suggest some other generator type.

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Tue Dec 23 11:04:07 2025
    From Newsgroup: comp.lang.c

    Am 22.12.2025 um 09:48 schrieb Michael Sanders:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    Use mt19993_64 ! ;-)

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael =?ISO-8859-1?Q?B=E4uerle?=@michael.baeuerle@stz-e.de to comp.lang.c on Tue Dec 23 11:09:02 2025
    From Newsgroup: comp.lang.c

    Janis Papanagnou wrote:
    On 2025-12-23 08:24, Michael Sanders wrote:

    [...]
    How do I bring up *posix only* man pages using 3?

    Sorry, can't help you here. Maybe someone else can.

    Manual pages are for the implementation of the operating system.

    The current standard version can be viewed here: <https://pubs.opengroup.org/onlinepubs/9799919799/functions/srand.html>
    This is the older standard version, still containing rand_r(): <https://pubs.opengroup.org/onlinepubs/9699919799/functions/srand.html>
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Dec 23 02:16:39 2025
    From Newsgroup: comp.lang.c

    Michael Sanders <porkchop@invalid.foo> writes:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:
    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    I see no difference when invoking any of:

    man 3 srand
    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?

    On Debian, Ubuntu, and similar systems, you can install the
    "manpages-posix" (section 1) and "manpages-posix-dev" (sections 3
    and 7) packages. You can then view the POSIX man page for srand
    by typing any of:

    man 3posix srand
    man -s 3posix srand
    man srand.3posix

    I'd expect similar packages to be available on (some) other systems.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Tue Dec 23 13:50:31 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 10:54:23 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-23 10:18, Michael S wrote:

    When I need PRNG then I am typically not deeply concerned about
    size of its internal state. On the other hand, I don't want to care
    about potentially insufficient randomness of the output (not in
    crypto sense). On the 3rd hand, vectors that I generate with PRNG
    tend to be big and I don't like to wait, so I do care somewhat
    about speed. Those 3 factors together plus availability long ago
    made MT19937-64 into my personal default PRNG of choice.

    I've never intensified my knowledge in direction of MT algorithms.


    MT19937-64 is available out of the box(*) in C++. But not in C, unfortunately.

    This is really strange given that the name ("Mersenne Twister") is
    that prominent.

    Looking that up I find at least "C" code for MT19937 in Wikipedia https://de.wikipedia.org/wiki/Mersenne-Twister
    It's based on 32 bit logic it seems; interpreting your "MT19937-64"
    I assume you're looking for a 64 bit based version?


    "Available out of the box" in this sentence means "part of standard
    library".
    Of course, MT19937-64 is available as 'C' source. But that's one more source+header to copy from project to project. Unlike in C++ where it's
    always here.


    At higher theoretical level MT is a generalization of LFSR, but it
    is not obvious when one looks at implementation.

    Well, at least there's the 'mod' operations all based on powers of 2
    along with all the binary op's which suggests some (non-arithmetic) bit-register type of algorithm, but the multiplication with 0x9908b0df
    (5 * 513496723) - which I'd suppose be hard to realize as/with LFSR -
    may suggest some other generator type.

    Janis



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Tue Dec 23 14:45:26 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 08:25:59 +0100, David Brown wrote:

    It is not the compilers that are different, it is the C standard
    libraries that are different. gcc is a compiler, not a library, but you
    are probably using glibc with it by default. musl is a library, not a compiler. There is no reason to suppose that different C standard
    libraries use the same implementation of rand()and srand(), so no reason
    to suppose they would give the same sequences - though each on their own will give a deterministic pseudo-random sequence based on their seeds.

    If you swap gcc with clang you will get the same results - it will
    depend on whether you are linking with glibc, musl, or another C library.

    Sure enough & thank you David - I appreciate your explanation.

    I see where my thinking was off now. You're 100% correct
    (I'm still learning as you noticed).
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Tue Dec 23 14:47:59 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 02:16:39 -0800, Keith Thompson wrote:

    On Debian, Ubuntu, and similar systems, you can install the
    "manpages-posix" (section 1) and "manpages-posix-dev" (sections 3
    and 7) packages. You can then view the POSIX man page for srand
    by typing any of:

    man 3posix srand
    man -s 3posix srand
    man srand.3posix

    I'd expect similar packages to be available on (some) other systems.

    Great! Thanks Keith, you sir are a walking encyclopedia.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Tue Dec 23 14:49:28 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 11:09:02 +0100 (CET), Michael B|nuerle wrote:

    Manual pages are for the implementation of the operating system.

    The current standard version can be viewed here: <https://pubs.opengroup.org/onlinepubs/9799919799/functions/srand.html>
    This is the older standard version, still containing rand_r(): <https://pubs.opengroup.org/onlinepubs/9699919799/functions/srand.html>

    Thank you for the pointers, I appreciate your insight.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Tue Dec 23 14:55:59 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 02:17:01 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random number. Seem
    easier and more portable across Linux & the *BSDs.

    Not to mention a lot stronger, cryptographically.

    No srand() combined with crypto on my end. Sounds like an invitation
    to get hacked from everything I've ever read about mixing the two.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Tue Dec 23 16:08:49 2025
    From Newsgroup: comp.lang.c

    Michael Sanders <porkchop@invalid.foo> writes:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    I see no difference when invoking any of:

    man 3 srand
    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?

    You're looking in the wrong place.

    https://pubs.opengroup.org/onlinepubs/9799919799/

    Select <System Interface> in the top left frame,
    select (3) in the subsequent bottom left frame and
    select the interface name in the bottom left frame. The
    manual page will be in the right frame.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/rand.html
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Tue Dec 23 16:13:18 2025
    From Newsgroup: comp.lang.c

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2025-12-23 08:24, Michael Sanders wrote:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    Sorry, can't help you here. Maybe someone else can.

    Myself I only access the Unix man pages as they come,
    i.e. using either 'man entry' or 'man section entry'.
    The POSIX information is usually textually integrated
    in the man pages.


    I see no difference when invoking any of:

    man 3 srand

    That's what I'm doing, and I see, for example,

    ...
    HISTORY
    rand()
    srand()
    SVr4, 4.3BSD, C89, POSIX.1-2001.

    Those interfaces were originally documented in the SVID
    (System V Interface Definition). The third edition (1989) states:

    "The function rand() uses a multiplicative congruential random-number
    generator with a period 2^32 that returns successive psuedo-random
    numbers in the range 0 to 32767."

    In the FUTURE DIRECTIONS section, it notes:

    "The algorithms used in rand() and srand() are obsolete and will
    be replaced with algorithms providing better pseudo-random characteristics
    in a future issue".

    There was never a fourth edition.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Tue Dec 23 17:54:05 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence are
    unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there were
    any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With sophisticated
    generator polynomials they produce also sequences of maximum period;
    say, for N=31 a non-repeating sequence of length 2^N - 1. The one
    element that is missing from the sequence is the 0 (that reproduces
    itself).

    Technically you pick some bit-values from fixed positions (depending
    on the generator polynomial) of the register and xor the bits to shift
    the result into the register. Here's ad hoc an example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to always produces the same sequence. There is no requirement that different
    seeds have to produce different sequences.
    So, for generator in your example, implementation like below would be
    fully legal. Personally, I wouldn't even consider it as particularly
    poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea for different reason: LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The process
    can be accelerate by generation of several bits at time via look up
    tables, but in order to get decent speed the table has to be rater big
    and using big tables in standard library is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential Generators, probably because for Stanadard library compactness of both
    code and data is considered more important than very high speed (not
    that on modern HW LCGs are slow) or superior random properties of
    Mersenne Twisters.

    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. OrCONeill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue Dec 23 19:15:24 2025
    From Newsgroup: comp.lang.c

    On 23/12/2025 15:45, Michael Sanders wrote:
    On Tue, 23 Dec 2025 08:25:59 +0100, David Brown wrote:

    It is not the compilers that are different, it is the C standard
    libraries that are different. gcc is a compiler, not a library, but you
    are probably using glibc with it by default. musl is a library, not a
    compiler. There is no reason to suppose that different C standard
    libraries use the same implementation of rand()and srand(), so no reason
    to suppose they would give the same sequences - though each on their own
    will give a deterministic pseudo-random sequence based on their seeds.

    If you swap gcc with clang you will get the same results - it will
    depend on whether you are linking with glibc, musl, or another C library.

    Sure enough & thank you David - I appreciate your explanation.

    I see where my thinking was off now. You're 100% correct
    (I'm still learning as you noticed).


    There are people who have been in this group for decades that still have trouble understanding the distinction between a C compiler, a C standard library, and a C implementation (which combines both). There are C
    compilers that have a standard library tightly attached or "built in" in
    the same product, and others which can work with a number of different C standard libraries. There are C standard libraries that only work with
    a single compiler, and others that are much more general - though you
    can't write a complete C standard library purely in fully portable C.
    And C compilers can implement standard library functions in the compiler itself (this is typically done for small functions like memcpy, or maths functions - things that can be significantly more efficient when handled inline by the compiler). There are many nuances involved - but you've
    found a clear way to show a difference between two common C libraries,
    so that's a good start.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From richard@richard@cogsci.ed.ac.uk (Richard Tobin) to comp.lang.c on Tue Dec 23 19:05:28 2025
    From Newsgroup: comp.lang.c

    In article <ycz2R.198208$79B9.129561@fx14.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:

    HISTORY
    rand()
    srand()
    SVr4, 4.3BSD, C89, POSIX.1-2001.

    Those interfaces were originally documented in the SVID
    (System V Interface Definition).

    Not "originally".

    The third edition (1989) states:

    "The function rand() uses a multiplicative congruential random-number
    generator with a period 2^32 that returns successive psuedo-random
    numbers in the range 0 to 32767."

    Unix 5th edition (page dated 1/15/73) says:

    Rand uses a multiplicative congruential random number generator to
    return successive pseudo-random numbers (in r0) in the range from 1
    to 2^15-1.

    (In those days they documented the assembler interface as well.)

    The SVID r3 text is almost identical to 7th edition's (1979).

    In the FUTURE DIRECTIONS section, it notes:

    "The algorithms used in rand() and srand() are obsolete and will
    be replaced with algorithms providing better pseudo-random characteristics
    in a future issue".

    There was never a fourth edition.

    There was, but it was post-Posix. Rather than having improved
    algorithms in [s]rand() it has "see also drand48".

    https://www.sco.com/developers/devspecs/vol1a.pdf

    -- Richard
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From John McCue@jmclnx@gmail.com.invalid to comp.lang.c on Tue Dec 23 20:37:42 2025
    From Newsgroup: comp.lang.c

    Ike Naar <ike@sdf.org> wrote:
    On 2025-12-23, John McCue <jmclnx@gmail.com.invalid> wrote:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    srand takes an unsigned argument.

    unsigned s;
    read(fd, &s, sizeof s);

    I am not quite sure what you are saying about srand(3).

    If you decide to read /dev/urandom, there is no need to
    call srand(3), the OS maintains random data itself. So
    read(2) will just return the random number of the type
    you want based upon the call.
    --
    [t]csh(1) - "An elegant shell, for a more... civilized age."
    - Paraphrasing Star Wars
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Wed Dec 24 00:08:24 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:
    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence
    are unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there
    were any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With
    sophisticated generator polynomials they produce also sequences of
    maximum period; say, for N=31 a non-repeating sequence of length
    2^N - 1. The one element that is missing from the sequence is the
    0 (that reproduces itself).

    Technically you pick some bit-values from fixed positions
    (depending on the generator polynomial) of the register and xor
    the bits to shift the result into the register. Here's ad hoc an
    example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to
    always produces the same sequence. There is no requirement that
    different seeds have to produce different sequences.
    So, for generator in your example, implementation like below would
    be fully legal. Personally, I wouldn't even consider it as
    particularly poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea
    for different reason: LFSR is a reasonably good PRNG for a single
    bit, but not when you want to generate a group of 31 pseudo-random
    bits. In order to get 31 new bits, without predictable repetitions
    from the previous value, you would have to do 31 steps. That's
    slow! The process can be accelerate by generation of several bits
    at time via look up tables, but in order to get decent speed the
    table has to be rater big and using big tables in standard library
    is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential Generators, probably because for Stanadard library compactness of
    both code and data is considered more important than very high
    speed (not that on modern HW LCGs are slow) or superior random
    properties of Mersenne Twisters.

    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. OrCONeill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.

    I don't know. Testing randomness is complicated matter.
    How can I be sure that LrCOEcuyer and SimardrCOs TestU01 suite tests things that I personally care about and that it does not test things that are
    of no interest for me? Especially, the latter.
    Also, the TestU01 suit is made for generators with 32-bit output.
    M. OrCONeill used ad hoc technique to make it applicable to generators
    with 64-bit output. Is this technique right? Or may be it put 64-bit
    PRNG at unfair disadvantage?
    Besides, I strongly disagree with at least one assertion made by
    OrCONeill: "While security-related applications should
    use a secure generator, because we cannot always know the future
    contexts in which our code will be used, it seems wise for all
    applications to avoid generators that make discovering their entire
    internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a defect.
    Anyway, even if I am skeptical about her criticism of popular PRNGs, intuitively I agree with the constructive part of the article -
    medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.
    On related note, I think that even simple counter fed into high quality
    hash function (not cryptographically high quality, far less than that)
    can produce excellent PRNG with even smaller internal state. But not
    very fast one. Although the speed depends on specifics of used
    computer. I can imagine computer that has low-latency Rijndael128
    instruction. On such computer, running counter through 3-4 rounds of
    Rijndael ill produce very good PRNG that is only 2-3 times slower than,
    for example, LCG 128/64.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Tue Dec 23 18:29:58 2025
    From Newsgroup: comp.lang.c

    On 2025-12-23 06:50, Michael S wrote:
    On Tue, 23 Dec 2025 10:54:23 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-23 10:18, Michael S wrote:
    ...
    MT19937-64 is available out of the box(*) in C++. But not in C,
    unfortunately.

    This is really strange given that the name ("Mersenne Twister") is
    that prominent.

    Looking that up I find at least "C" code for MT19937 in Wikipedia
    https://de.wikipedia.org/wiki/Mersenne-Twister
    It's based on 32 bit logic it seems; interpreting your "MT19937-64"
    I assume you're looking for a 64 bit based version?


    "Available out of the box" in this sentence means "part of standard
    library".

    Citation, please? I can find neither Mersenne nor "MT19937-64" anywhere
    in n5001.pdf, the latest draft version of the C++ standard that I have
    access to, which is dated 2024-12-17.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Dec 23 16:30:16 2025
    From Newsgroup: comp.lang.c

    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 2025-12-23 06:50, Michael S wrote:
    On Tue, 23 Dec 2025 10:54:23 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-23 10:18, Michael S wrote:
    ...
    MT19937-64 is available out of the box(*) in C++. But not in C,
    unfortunately.

    This is really strange given that the name ("Mersenne Twister") is
    that prominent.

    Looking that up I find at least "C" code for MT19937 in Wikipedia
    https://de.wikipedia.org/wiki/Mersenne-Twister
    It's based on 32 bit logic it seems; interpreting your "MT19937-64"
    I assume you're looking for a 64 bit based version?


    "Available out of the box" in this sentence means "part of standard
    library".

    Citation, please? I can find neither Mersenne nor "MT19937-64" anywhere
    in n5001.pdf, the latest draft version of the C++ standard that I have
    access to, which is dated 2024-12-17.

    N5001 29.5.4.3 [rand.eng.mers] "Class template mersenne_twister_engine".

    N5001 29.5.6 [rand.predef] "Engines and engine adaptors with predefined parameters" defines "mt19937" and "mt19937_64".

    Its description of the algorithm isn't very detailed, but it
    does impose some very specific requirements. For mt19937:
    "Required behavior: The 10000th consecutive invocation of a
    default-constructed object of type mt19937 produces the value
    4123659995.". For mt19937_64: "Required behavior: The 10000th
    consecutive invocation of a default-constructed object of type
    mt19937_64 produces the value 9981545732273789042."

    If we're going to discuss this in any more detail (rather than
    discussing random numbers in general), I suggest comp.lang.c++.

    As already mentioned, there are implementations of mt19937 for C,
    but unlike in C++ they aren't part of the standard library.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed Dec 24 02:02:59 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 00:08:24 +0200, Michael S wrote:

    Testing randomness is complicated matter.

    Impossible, really, if you define rCLrandomrCY as rCLNobody can know what comes
    nextrCY.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Tue Dec 23 23:43:19 2025
    From Newsgroup: comp.lang.c

    On 2025-12-23 21:02, Lawrence DrCOOliveiro wrote:
    On Wed, 24 Dec 2025 00:08:24 +0200, Michael S wrote:

    Testing randomness is complicated matter.

    Impossible, really, if you define rCLrandomrCY as rCLNobody can know what comes
    nextrCY.

    The quality of pseudo-random number generators can be measured, but you
    need to carefully define what you mean by "quality". The relevant
    measures can be different for different purposes. I've seen a randome
    number generator used in a context where the only relevant criteria was
    that the probability of each number occurring was equal. In that
    particular contest, a function that simply always returned the sequence
    0, 1, 2, ... RAND_MAX, and then started over again at the beginning
    would have been good enough. Most applications have somewhat stronger requirements :-)

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed Dec 24 05:34:40 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 23:43:19 -0500, James Kuyper wrote:

    On 2025-12-23 21:02, Lawrence DrCOOliveiro wrote:

    On Wed, 24 Dec 2025 00:08:24 +0200, Michael S wrote:

    Testing randomness is complicated matter.

    Impossible, really, if you define rCLrandomrCY as rCLNobody can know what
    comes nextrCY.

    The quality of pseudo-random number generators can be measured, but you
    need to carefully define what you mean by "quality".

    ThatrCOs not exactly disagreeing with what IrCOm saying ...
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Tue Dec 23 21:44:04 2025
    From Newsgroup: comp.lang.c

    On 12/22/2025 12:48 AM, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    Forgive me for C++, but this RNG of mine might be useful for detecting
    the state of a system:

    https://groups.google.com/g/comp.lang.c++/c/7u_rLgQe86k/m/fYU9SnuAFQAJ
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed Dec 24 06:16:51 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC), Michael Sanders wrote:

    Wish there was such a 'device' under Windows...

    You should get one if you install WSL2.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Wed Dec 24 10:51:14 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed Dec 24 00:59:16 2025
    From Newsgroup: comp.lang.c

    On 12/24/2025 12:51 AM, Michael S wrote:
    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom



    CSPRNG, vs an actual TRNG?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Wed Dec 24 09:00:50 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> wrote:
    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence
    are unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there
    were any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With
    sophisticated generator polynomials they produce also sequences of
    maximum period; say, for N=31 a non-repeating sequence of length
    2^N - 1. The one element that is missing from the sequence is the
    0 (that reproduces itself).

    Technically you pick some bit-values from fixed positions
    (depending on the generator polynomial) of the register and xor
    the bits to shift the result into the register. Here's ad hoc an
    example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to
    always produces the same sequence. There is no requirement that
    different seeds have to produce different sequences.
    So, for generator in your example, implementation like below would
    be fully legal. Personally, I wouldn't even consider it as
    particularly poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea
    for different reason: LFSR is a reasonably good PRNG for a single
    bit, but not when you want to generate a group of 31 pseudo-random
    bits. In order to get 31 new bits, without predictable repetitions
    from the previous value, you would have to do 31 steps. That's
    slow! The process can be accelerate by generation of several bits
    at time via look up tables, but in order to get decent speed the
    table has to be rater big and using big tables in standard library
    is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential
    Generators, probably because for Stanadard library compactness of
    both code and data is considered more important than very high
    speed (not that on modern HW LCGs are slow) or superior random
    properties of Mersenne Twisters.

    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. OrCONeill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.


    I don't know. Testing randomness is complicated matter.
    How can I be sure that LrCOEcuyer and SimardrCOs TestU01 suite tests things that I personally care about and that it does not test things that are
    of no interest for me? Especially, the latter.

    It is extremaly unlikely that TestU only tests things that are
    important to you. However, IMO value of such test is that
    generator which passes the test avoids several common traps.
    If any of them is relevant for you, generator will avoid it.
    Of course, you may have _very_ special situation with
    extremaly uncommon problem, but this is unlikely and anyway
    in such case you probably should extensively test generator
    that you want to use.

    Also, the TestU01 suit is made for generators with 32-bit output.
    M. OrCONeill used ad hoc technique to make it applicable to generators
    with 64-bit output. Is this technique right? Or may be it put 64-bit
    PRNG at unfair disadvantage?

    My point of view is that generator can be used to generate long
    bistream. Then you can cut the bitstream and get number of
    desired size. Good tests should check that such usage leads
    to reasonable properties. So, fact that one generator produces
    32-bit pieces and other produces 64-bit pieces should be irrelevant
    to the test.

    Besides, I strongly disagree with at least one assertion made by
    OrCONeill: "While security-related applications should
    use a secure generator, because we cannot always know the future
    contexts in which our code will be used, it seems wise for all
    applications to avoid generators that make discovering their entire
    internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a defect.

    OrCONeill is not a prophet, ignore what she say it you think you
    know better (which is probably the above).

    Anyway, even if I am skeptical about her criticism of popular PRNGs, intuitively I agree with the constructive part of the article - medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    She seem to care very much about having minimal possible state.
    That is may be nice on embeded systems, but in general I would
    happily accept slighty bigger state (say 256 bits). But if
    we can get good properties with very small state, then why not?
    After all looking at state and updating it takes code, so
    small state helps with having fast generator.

    Concerning Mersenne Twister, she is not the only one to
    criticise it. My personal opinion is that given large
    state and not so simple update Mersenne Twister would
    have to be very very good to justify its use. But it
    fails some tests, so does not look _better_ than other
    generators.

    On related note, I think that even simple counter fed into high quality
    hash function (not cryptographically high quality, far less than that)
    can produce excellent PRNG with even smaller internal state. But not
    very fast one. Although the speed depends on specifics of used
    computer. I can imagine computer that has low-latency Rijndael128 instruction. On such computer, running counter through 3-4 rounds of
    Rijndael ill produce very good PRNG that is only 2-3 times slower than,
    for example, LCG 128/64.

    Maybe.
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Wed Dec 24 12:12:11 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 09:00:50 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:
    Michael S <already5chosen@yahoo.com> wrote:
    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:


    Also, the TestU01 suit is made for generators with 32-bit output.
    M. OrCONeill used ad hoc technique to make it applicable to generators
    with 64-bit output. Is this technique right? Or may be it put 64-bit
    PRNG at unfair disadvantage?

    My point of view is that generator can be used to generate long
    bistream. Then you can cut the bitstream and get number of
    desired size. Good tests should check that such usage leads
    to reasonable properties. So, fact that one generator produces
    32-bit pieces and other produces 64-bit pieces should be irrelevant
    to the test.

    What you say is correct in few use cases. But there are many uses
    cases (in field of testing of numeric code, probably, most of them)
    in which "less random" LS bits are acceptable.
    Not that I can see why it could be the case for MT19937-64, but it
    could apply to one of two of other 64-bit generators tested by O'Neill.
    Besides, I strongly disagree with at least one assertion made by
    OrCONeill: "While security-related applications should
    use a secure generator, because we cannot always know the future
    contexts in which our code will be used, it seems wise for all
    applications to avoid generators that make discovering their entire internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a
    defect.

    OrCONeill is not a prophet, ignore what she say it you think you
    know better (which is probably the above).

    Anyway, even if I am skeptical about her criticism of popular PRNGs, intuitively I agree with the constructive part of the article - medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    She seem to care very much about having minimal possible state.
    That is may be nice on embeded systems, but in general I would
    happily accept slighty bigger state (say 256 bits). But if
    we can get good properties with very small state, then why not?
    After all looking at state and updating it takes code, so
    small state helps with having fast generator.

    Agreed.
    Concerning Mersenne Twister, she is not the only one to
    criticise it. My personal opinion is that given large
    state and not so simple update Mersenne Twister would
    have to be very very good to justify its use.
    One theoretical advantage of MT19937 is that it has period of astronomic proportions. Which means that one instance of PRNG could be
    de-multiplexed into millions or billions of sub-streams with no
    detectable degradation of the quality of each sub-stream.
    However I fail to see how de-multiplexing into more than ~ one
    thousand of sub-streams can be practical. And for the latter one does
    not need to be astronomical, something like period=2**96 would be
    fully sufficient with many bits to spare.
    So, in theory I agree with the criticism. But in practice I am not
    bothered by the size of MT state.
    But it
    fails some tests, so does not look _better_ than other
    generators.

    It would be interesting to find out what were those tests that failed.
    I wonder, if tests suit can run faster on multicore computer. I don't
    want to wait 5-6 hours just to find out that report does not provide an information that I am looking for.
    On related note, I think that even simple counter fed into high
    quality hash function (not cryptographically high quality, far less
    than that) can produce excellent PRNG with even smaller internal
    state. But not very fast one. Although the speed depends on
    specifics of used computer. I can imagine computer that has
    low-latency Rijndael128 instruction. On such computer, running
    counter through 3-4 rounds of Rijndael ill produce very good PRNG
    that is only 2-3 times slower than, for example, LCG 128/64.

    Maybe.

    May be I'd even test my hypothesis. Eventually. Except that, again, I
    am not thrilled by idea of waiting 6 hours for each result.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Wed Dec 24 05:22:11 2025
    From Newsgroup: comp.lang.c

    On 12/23/2025 11:54 AM, Waldek Hebisch wrote:
    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence are
    unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there were
    any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With sophisticated
    generator polynomials they produce also sequences of maximum period;
    say, for N=31 a non-repeating sequence of length 2^N - 1. The one
    element that is missing from the sequence is the 0 (that reproduces
    itself).

    Technically you pick some bit-values from fixed positions (depending
    on the generator polynomial) of the register and xor the bits to shift
    the result into the register. Here's ad hoc an example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to always
    produces the same sequence. There is no requirement that different
    seeds have to produce different sequences.
    So, for generator in your example, implementation like below would be
    fully legal. Personally, I wouldn't even consider it as particularly
    poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea for
    different reason: LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The process
    can be accelerate by generation of several bits at time via look up
    tables, but in order to get decent speed the table has to be rater big
    and using big tables in standard library is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential
    Generators, probably because for Stanadard library compactness of both
    code and data is considered more important than very high speed (not
    that on modern HW LCGs are slow) or superior random properties of
    Mersenne Twisters.

    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. OrCONeill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.


    A lot can depend on what one wants as well...

    Fast/Simple:
    seed=seed*65521+17;
    val=(seed>>16)&32767;

    At first glance, this approach seems random enough, but these type of
    RNGs have a type of repeating pattern that can become obvious, say, if
    using them to generate random noise images.



    Or, can also work OK (also fast/simple):
    seed=(seed<<1)^(~(seed>>7));
    val=(seed>>8)&32767;

    Some people seem to really like using lookup tables.

    64-bit multiply can potentially be very slow, and multiply in general
    isn't always cheap, so can make sense to avoid using it if not necessary.

    So, shift-and-XOR is fast, above approach is also trivially extended to
    64 bits.

    Its randomness can be improved somewhat (at the cost of speed), say:
    seed1=(seed1<<1)^(~(seed1>>13));
    seed2=(seed2<<3)^(~(seed2>>19));
    seed1^=seed2>>23;
    seed2^=seed1>>23;
    val=(seed1>>11)^(seed2>>11);
    val=(val^(val>>17))&32767;

    Where seed1 and seed2 are two 64-bit values.

    Not much formal testing here, mostly just sort of approaches that seemed
    to work OK IME.



    Had also noted that there are ways to do checksums that are a lot faster
    and simpler than more widespread algorithms and also seem to still do reasonably well at error detection.

    Say, for example:
    sum1=1; sum2=1;
    for(i=0; i<szWords; i++)
    { sum1+=data[i]; sum2+=sum1; }
    sum1=((uint32_t)sum1)+(sum1>>32);
    sum2=((uint32_t)sum2)+(sum2>>32);
    csum=sum1^sum2;

    Where, sum1/sum2 are 64-bit, and data is interpreted as 32-bit words,
    all unsigned.

    But, yeah...

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 24 15:21:11 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 06:16:51 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Wish there was such a 'device' under Windows...

    You should get one if you install WSL2.

    To be fair there is the 'Windows entropy pool' & its
    non-deterministic too but its only available via API.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Ike Naar@ike@sdf.org to comp.lang.c on Wed Dec 24 15:22:47 2025
    From Newsgroup: comp.lang.c

    On 2025-12-23, John McCue <jmclnx@gmail.com.invalid> wrote:
    Ike Naar <ike@sdf.org> wrote:
    On 2025-12-23, John McCue <jmclnx@gmail.com.invalid> wrote:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    srand takes an unsigned argument.

    unsigned s;
    read(fd, &s, sizeof s);

    I am not quite sure what you are saying about srand(3).

    If you decide to read /dev/urandom, there is no need to
    call srand(3), the OS maintains random data itself. So
    read(2) will just return the random number of the type
    you want based upon the call.

    Sorry, my misunderstanding. I thought you wanted to use a random
    number (from /dev/urandom instead of time(NULL)) to seed srand().
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 24 15:28:24 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 10:51:14 +0200, Michael S wrote:

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom

    Was referring to the concept of a device in the same idiom of BSD/Linux/Apple...

    Something that is just as easy to use.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 24 15:41:13 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 21:44:04 -0800, Chris M. Thomasson wrote:

    On 12/22/2025 12:48 AM, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    Forgive me for C++, but this RNG of mine might be useful for detecting
    the state of a system:

    https://groups.google.com/g/comp.lang.c++/c/7u_rLgQe86k/m/fYU9SnuAFQAJ

    My aims (mostly just learning my around C at this point)
    are *much* more simple. I needed something that is seed-able/deterministic/portable allowing the user a
    shot at replaying a round in a silly game I've been
    working on every now & again:

    int genseed(int seed_in) {
    if (seed_in >= 10000000) return seed_in;
    unsigned long t = (unsigned long)time(NULL);
    unsigned long c = (unsigned long)clock();
    return (int)(((t ^ c) % 80000000UL) + 10000000UL);
    }
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 24 15:44:33 2025
    From Newsgroup: comp.lang.c

    On Tue, 23 Dec 2025 16:08:49 GMT, Scott Lurndal wrote:

    Michael Sanders <porkchop@invalid.foo> writes:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    I see no difference when invoking any of:

    man 3 srand
    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?

    You're looking in the wrong place.

    https://pubs.opengroup.org/onlinepubs/9799919799/

    Select <System Interface> in the top left frame,
    select (3) in the subsequent bottom left frame and
    select the interface name in the bottom left frame. The
    manual page will be in the right frame.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/rand.html

    Thank you Scott.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Wed Dec 24 17:44:52 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 15:28:24 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Wed, 24 Dec 2025 10:51:14 +0200, Michael S wrote:

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom


    Was referring to the concept of a device in the same idiom of BSD/Linux/Apple...

    Something that is just as easy to use.


    What is not easy in the functions referred above? You do the same
    couple of steps as on Unix: open device then read few bytes from it.
    Only names are different.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 24 16:17:03 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 17:44:52 +0200, Michael S wrote:

    On Wed, 24 Dec 2025 15:28:24 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Wed, 24 Dec 2025 10:51:14 +0200, Michael S wrote:

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7:
    https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later:
    https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom


    Was referring to the concept of a device in the same idiom of
    BSD/Linux/Apple...

    Something that is just as easy to use.


    What is not easy in the functions referred above? You do the same
    couple of steps as on Unix: open device then read few bytes from it.
    Only names are different.

    Depends the toolkit no?

    Unix:
    $ head -c 8 /dev/urandom | od -An | tr -d ' '
    4fa2c3d17b9a8f12

    Windows:
    PS C:\Users\Bob>
    $bytes = New-Object byte[] 8 [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) [Console]::Write($bytes | ForEach-Object { "{0:x2}" -f $_ })
    --
    :wq
    Mike Sanders
    es
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Wed Dec 24 17:53:54 2025
    From Newsgroup: comp.lang.c

    On 2025-12-24 17:17, Michael Sanders wrote:

    Unix:
    $ head -c 8 /dev/urandom | od -An | tr -d ' '
    4fa2c3d17b9a8f12

    Windows:
    PS C:\Users\Bob>
    $bytes = New-Object byte[] 8 [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
    [Console]::Write($bytes | ForEach-Object { "{0:x2}" -f $_ })

    Amazing! 8-o

    Or rather; frightening! ("The little Shop of Horrors")
    A mixture (best/worst) of all; OO, Functional, and Shell?

    What is that; "Powershell", or something else?
    (I've mostly ignored Windows during the past 20+ years.)

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Wed Dec 24 18:04:05 2025
    From Newsgroup: comp.lang.c

    On 2025-12-24 16:41, Michael Sanders wrote:

    My aims (mostly just learning my around C at this point)
    are *much* more simple. I needed something that is seed-able/deterministic/portable allowing the user a
    shot at replaying a round in a silly game I've been
    working on every now & again:

    int genseed(int seed_in) {
    if (seed_in >= 10000000) return seed_in;
    unsigned long t = (unsigned long)time(NULL);
    unsigned long c = (unsigned long)clock();
    return (int)(((t ^ c) % 80000000UL) + 10000000UL);
    }


    If you need a portable function across different platforms
    you may want to write an own random() function, code based
    on some simple, proven algorithm. Or borrow a piece of code
    from some existing public source code library.

    For "_replaying_ a round in a silly game" across platforms
    (or generally) you should not seed it with time() or other
    random factors (as shown in your code snippet).

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Wed Dec 24 17:08:48 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 24 Dec 2025 15:28:24 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Wed, 24 Dec 2025 10:51:14 +0200, Michael S wrote:

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7:
    https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later:
    https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom


    Was referring to the concept of a device in the same idiom of
    BSD/Linux/Apple...

    Something that is just as easy to use.


    What is not easy in the functions referred above? You do the same
    couple of steps as on Unix: open device then read few bytes from it.
    Only names are different.

    Even easier in the GCC; one can just generate the rdrand instruction directly for intel targets:

    unsigned int __builtin_ia32_rdrand16_step (unsigned short *);
    unsigned int __builtin_ia32_rdrand32_step (unsigned int *);
    unsigned int __builtin_ia32_rdrand64_step (unsigned long long *);

    ARM64 provides a system register (RNDR) which is accessible at
    all exception levels. The Neoverse implementations provide implementation-defined system registers that convert the
    read of the register into a bus transaction to a device which
    satisfies the random number request (set up by the boot firmware);
    so the characteristics of the hardware generator are specific to
    an implementation of a neoverse core.

    int __builtin_arm_rndr(uint64_t *val);
    int __builtin_arm_rndrrs(uint64_t *val);
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 24 17:27:32 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 17:53:54 +0100, Janis Papanagnou wrote:

    On 2025-12-24 17:17, Michael Sanders wrote:

    Unix:
    $ head -c 8 /dev/urandom | od -An | tr -d ' '
    4fa2c3d17b9a8f12

    Windows:
    PS C:\Users\Bob>
    $bytes = New-Object byte[] 8
    [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
    [Console]::Write($bytes | ForEach-Object { "{0:x2}" -f $_ })

    Amazing! 8-o

    Or rather; frightening! ("The little Shop of Horrors")
    A mixture (best/worst) of all; OO, Functional, and Shell?

    What is that; "Powershell", or something else?
    (I've mostly ignored Windows during the past 20+ years.)

    Janis

    Yes its Powershell. You nailed it spot on IMO its terrible Janis.
    *Speaking only for myself* it's: thick/crufty/oop/functional/shell
    all mashed together like sludge. Awful stuff.

    And plain old 'cmd.exe' (more or less the Windows equivalent
    of /bin/sh) has evolved oddly too with all its 'syntactical sugar'...

    witness: echo hello world? | | tr 'A-Za-z' 'N-ZA-Mn-za-m'

    vs.: <https://rosettacode.org/wiki/Rot-13#Batch_File>

    I'm breaking free from it =)

    This tool helps: <https://frippery.org/busybox/>
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 24 17:33:23 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 17:27:32 -0000 (UTC), Michael Sanders wrote:

    witness: echo hello world? | | tr 'A-Za-z' 'N-ZA-Mn-za-m'

    typo, should be: echo hello world? | tr 'A-Za-z' 'N-ZA-Mn-za-m'
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Wed Dec 24 20:16:22 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 17:33:23 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Wed, 24 Dec 2025 17:27:32 -0000 (UTC), Michael Sanders wrote:

    witness: echo hello world? | | tr 'A-Za-z' 'N-ZA-Mn-za-m'

    typo, should be: echo hello world? | tr 'A-Za-z' 'N-ZA-Mn-za-m'


    I agree that Powershell is too complicated and too "wannabe real
    programming language" which makes it bad shell scripting language.
    Esp. so for quick throwaway scripts.

    However I don't quite understand what you find wrong with cmd.exe.
    Cryptic? May be. But I can not imagine shell scripting language which is
    not cryptic in some way.
    Has few limitations that one would not expect in shell script in 2025?
    Yes.
    set a= 1000000
    set /A ab= %a% * 1000
    1000000000
    set /A ab= %a% * 2000
    2000000000
    set /A ab= %a% * 3000
    -1294967296

    But cmd.exe language certainly is *not* over-complicated. Rather more
    like too primitive.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed Dec 24 19:00:32 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 15:21:11 -0000 (UTC), Michael Sanders wrote:

    On Wed, 24 Dec 2025 06:16:51 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Wish there was such a 'device' under Windows...

    You should get one if you install WSL2.

    To be fair there is the 'Windows entropy pool' & its
    non-deterministic too but its only available via API.

    You begin to see why Microsoft is supporting Linux more and more.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed Dec 24 13:48:19 2025
    From Newsgroup: comp.lang.c

    On 12/23/2025 2:08 PM, Michael S wrote:
    [...]
    I don't know. Testing randomness is complicated matter.

    [...]

    Fwiw, ent is a nice little program:

    https://www.fourmilab.ch/random/


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Thu Dec 25 02:01:11 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 20:16:22 +0200, Michael S wrote:

    I agree that Powershell is too complicated and too "wannabe real
    programming language" which makes it bad shell scripting language.
    Esp. so for quick throwaway scripts.

    However I don't quite understand what you find wrong with cmd.exe.
    Cryptic? May be. But I can not imagine shell scripting language which is
    not cryptic in some way.
    Has few limitations that one would not expect in shell script in 2025?
    Yes.
    set a= 1000000
    set /A ab= %a% * 1000
    1000000000
    set /A ab= %a% * 2000
    2000000000
    set /A ab= %a% * 3000
    -1294967296

    But cmd.exe language certainly is *not* over-complicated. Rather more
    like too primitive.

    Complicated doesn't quite capture the zeitgeist, closer to obtuse,
    or baroque. An example below of a valid block that makes Perl look
    good... Notice for instance the alternate use of set compared to
    your example.

    yours: set [option] var = assignment

    below: set "var = assignment"

    Not to mention some other quirks. But that's just my opinion.

    @echo off

    cd /d "%~dp0"
    nul chcp 65001
    set "_MediaInfo=C:\Program Files\MediaInfo\MediaInfo.exe"

    for /f ^usebackq^delims^= %%G in =;(` "%__AppDir__%where.exe" .:v*.mpg `);= do for /f usebackq^delims^= %%i in =;(`
    call "%_MediaInfo%" --inform^^^=Video^^^;^^^%%Height^^^%% "%%~fG" `);= do rename "%%~fG" "%%~nG-%%~ip%%~xG"

    nul chcp 437
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Thu Dec 25 03:17:41 2025
    From Newsgroup: comp.lang.c

    On Thu, 25 Dec 2025 02:01:11 -0000 (UTC), Michael Sanders wrote:

    Not to mention some other quirks. But that's just my opinion.

    Nothing specific to either cmd.exe or PowerShell, but the Windows
    command line is fundamentally broken. This is because it derives from
    the CP/M command line model, which in turn was inherited from old-time
    DEC operating systems.

    On these DEC systems, the command line was a simple string buffer. So
    there is this assumption that program invocation is always going to be
    mediated by some kind of rCLshellrCY program, and the concept of one
    program directly invoking another is either nonexistent, or only
    grudgingly tolerated.

    Contrast this with the Unix approach, where the command line is an
    array of separate string rCLwordsrCY. There is no rCLshellrCY that occupies a privileged place in the system; any program can directly invoke any
    other, without having to worry about properly escaping any special
    characters that might be (mis)interpreted by some rCLshellrCY.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Thu Dec 25 04:30:45 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 17:53:54 +0100, Janis Papanagnou wrote:

    [...]

    Ok seed is working for replaying a game. =)
    Pure C, no ncurses etc, built in help too.

    You know this game don't you Janis?
    Simple little project. Screenshot...

    <https://drive.google.com/file/d/1dKSjDzmu0mLy76GWrlZUT9HVK72GiP9d/view>
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Wed Dec 24 23:09:25 2025
    From Newsgroup: comp.lang.c

    On 12/24/2025 5:22 AM, BGB wrote:
    On 12/23/2025 11:54 AM, Waldek Hebisch wrote:
    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence are
    unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there were
    any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With sophisticated
    generator polynomials they produce also sequences of maximum period;
    say, for N=31 a non-repeating sequence of length 2^N - 1. The one
    element that is missing from the sequence is the 0 (that reproduces
    itself).

    Technically you pick some bit-values from fixed positions (depending
    on the generator polynomial) of the register and xor the bits to shift >>>> the result into the register. Here's ad hoc an example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    -a-a-a-a-a uint32_t init = 0x00000038;
    -a-a-a-a-a uint32_t reg = init;
    -a-a-a-a-a uint32_t new_bit;
    -a-a-a-a-a int count = 0;
    -a-a-a-a-a do {
    -a-a-a-a-a-a-a-a-a new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    -a-a-a-a-a-a-a-a-a reg <<= 1;
    -a-a-a-a-a-a-a-a-a reg |= new_bit;
    -a-a-a-a-a-a-a-a-a reg &= 0x7fffffff;
    -a-a-a-a-a-a-a-a-a count++;
    -a-a-a-a-a } while (reg != init);
    -a-a-a-a-a printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to always
    produces the same sequence. There is no requirement that different
    seeds have to produce different sequences.
    So, for generator in your example, implementation like below would be
    fully legal. Personally, I wouldn't even consider it as particularly
    poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea for
    different reason: LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The process
    can be accelerate by generation of several bits at time via look up
    tables, but in order to get decent speed the table has to be rater big
    and using big tables in standard library is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential
    Generators, probably because for Stanadard library compactness of both
    code and data is considered more important than very high speed (not
    that on modern HW LCGs are slow) or superior random properties of
    Mersenne Twisters.

    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. OrCONeill where she gives a family of algorithms and runs
    several statistical tests against known algorithms.-a Mersenne
    Twister does not look good in tests.-a If you have enough (128) bits
    LCGs do pass tests.-a A bunch of generators with 64-bit state also
    passes tests.-a So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries.-a Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.


    A lot can depend on what one wants as well...

    Fast/Simple:
    -a seed=seed*65521+17;
    -a val=(seed>>16)&32767;

    At first glance, this approach seems random enough, but these type of
    RNGs have a type of repeating pattern that can become obvious, say, if
    using them to generate random noise images.


    Turns out more true of the one below than the one above, though both are
    still pretty weak.


    Usually, one very quick/dirty way to test a RNG is to generate an image
    filled with random numbers:
    If it looks like white noise, the RNG works OK;
    If the image has repeating patterns or any other sort of obvious
    structure, the RNG is broken.

    Not a great test, but for many practical use cases may be good enough.

    The above example shows evidence of "structural" features, implying the
    RNG may not be particularly strong.



    Or, can also work OK (also fast/simple):
    -a seed=(seed<<1)^(~(seed>>7));
    -a val=(seed>>8)&32767;


    Got around to testing, this particular one was kinda broken (breaks down
    into repeating patterns).


    A tweak, say:
    seed=(seed<<1)^(~(seed>>7))^(seed>>21);
    val=(seed>>8)&32767;
    Is much better...

    Also seems to work:
    seed=(seed<<3)^(~(seed>>19))^(seed>>13);
    Etc.



    Seems the simple pattern with one XOR may be too little, and at least
    two XORs may be needed here. But, this approach is still fragile and
    prone to break down into repeating patterns. Also seems to need a mix of
    a prime and non-prime for some reason.


    A lot may depend on on how quickly the random numbers need to be
    generated vs the quality of the needed randomness (and whether or not
    the target machine has a fast integer multiply).

    Where, say, one down-side of more traditional "multiply 64-bit value by constant" RNGs is that performance suffers badly in the absence of fast
    64-bit multiply.

    one other tradeoff being if the target machine is natively 32 or 64 bit.


    In some cases, it may make sense to do a fast RNG with a simpler
    algorithm, but then ever N numbers, reseed the fast RNG with a number generated by a slower but more powerful RNG.

    Where, say, fast RNG may be needed for things like genetic algorithms
    where often the performance of the mutation step depends highly on
    having fast random numbers (with the actual goodness of the RNG being secondary, but a certain minimum is needed to avoid the process breaking down).



    Some people seem to really like using lookup tables.


    I don't really get the point of lookup table driven RNGs.

    A lot of these ones are, say, a table with 256 spots, and an index.
    Each time one generates a random number (usually a byte), it returns the
    value at that location in the table and advances to the next index.

    Sometimes some get clever and use an algorithm to jitter the table index.

    I have mostly seen this strategy used in old game engines.

    These typically fail the image test as by design they give repeating
    patterns.



    64-bit multiply can potentially be very slow, and multiply in general
    isn't always cheap, so can make sense to avoid using it if not necessary.

    So, shift-and-XOR is fast, above approach is also trivially extended to
    64 bits.

    Its randomness can be improved somewhat (at the cost of speed), say:
    -a seed1=(seed1<<1)^(~(seed1>>13));
    -a seed2=(seed2<<3)^(~(seed2>>19));
    -a seed1^=seed2>>23;
    -a seed2^=seed1>>23;
    -a val=(seed1>>11)^(seed2>>11);
    -a val=(val^(val>>17))&32767;

    Where seed1 and seed2 are two 64-bit values.

    Not much formal testing here, mostly just sort of approaches that seemed
    to work OK IME.


    The strategy used above is closer to what I had often used IME.

    If more randomness is needed, the pattern can be extended to 4 seeds and
    with more variability in the shifts.

    Or, can also be trimmed down if it needs to be faster.



    Had also noted that there are ways to do checksums that are a lot faster
    and simpler than more widespread algorithms and also seem to still do reasonably well at error detection.

    Say, for example:
    -a sum1=1; sum2=1;
    -a for(i=0; i<szWords; i++)
    -a-a-a { sum1+=data[i]; sum2+=sum1; }
    -a sum1=((uint32_t)sum1)+(sum1>>32);
    -a sum2=((uint32_t)sum2)+(sum2>>32);
    -a csum=sum1^sum2;

    Where, sum1/sum2 are 64-bit, and data is interpreted as 32-bit words,
    all unsigned.


    A variant running 2 or 4 sets of sums in parallel can also be faster,
    but the single pair of sums demonstrates the basic idea.

    Though, special handling may be needed if the data is not a multiple of
    the working size (if this needs to be handled, one may need special case
    logic to deal with the last N bytes).


    Note here that a single linear sum of all the values, while fast, is
    often unreasonably weak (and will miss many types of errors).

    A second sum of the first sum can greatly increase error detection.

    While, at the same time, being significantly faster than something like
    CRC32 (one may find that for various use-cases, things like CRC32 or
    Adler32 checksums may be unacceptably slow).



    I have yet to find an ideally fast strategy for hashing strings.

    One simple strategy is, say:
    h=0; cs=str;
    while(*cs)h=h*251+(*cs++);
    h=h*251+1;
    v=(h>>8)&4095; //or, whatever is the size of the hash table.

    But, this isn't particularly fast.

    But, then another problem is that strings are usually short and one
    doesn't know the length in advance. Using "strlen()" also being not
    exactly fast either (trying to use "strlen()") may be slower than the
    naive approach.

    one variation of the above is, say:
    cs=str+1; c=*str; h=0;
    while(c) { h=h*251+c; c=*cs++; }
    Or, say:
    cs=str+1; c=*str; h=0;
    while(c) { h=(h<<7)+(c-h); c=*cs++; }
    Or, ...

    But, gains are small, and usually not really an obvious way to speed it
    up when the majority of string lengths are between 3 and 7 characters
    (most strategies being more effective against longer strings).

    while, at least, excluding wonk like:
    c0=str[0]; c1=str[1]; ...
    if(!c0) { return(0); }
    if(!c1) { return(c0); }
    if(!c2) { return((c0*31+c1)&4095); }
    ...
    Then falling back to a generic strategy for 8+ characters.


    ...


    But, yeah...


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Wed Dec 24 23:35:55 2025
    From Newsgroup: comp.lang.c

    On 12/23/2025 8:55 AM, Michael Sanders wrote:
    On Tue, 23 Dec 2025 02:17:01 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random number. Seem
    easier and more portable across Linux & the *BSDs.

    Not to mention a lot stronger, cryptographically.

    No srand() combined with crypto on my end. Sounds like an invitation
    to get hacked from everything I've ever read about mixing the two.


    While arguably a typical C library "rand()" isn't that strong, if one
    has a number sequence of output random digits, it might still take an impractical amount of time to brute-force search the entire seed space
    for a 64-bit seed.


    So, for example, if used to encrypt a point-to-point session, it is
    likely whatever session would have ended long before the attacker could brute-force the pattern.

    And, AFAIK, with a typical LCG there is no good way to feed the numbers
    back into the RNG to get back the seed (though, such a strategy is
    possible with some of my shift-and-XOR RNG's, but these are usually more intended to generate numbers faster, as the C library "rand()" is
    sometimes not fast enough...).


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Thu Dec 25 05:41:39 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 18:04:05 +0100, Janis Papanagnou wrote:

    On 2025-12-24 16:41, Michael Sanders wrote:

    My aims (mostly just learning my around C at this point)
    are *much* more simple. I needed something that is
    seed-able/deterministic/portable allowing the user a
    shot at replaying a round in a silly game I've been
    working on every now & again:

    int genseed(int seed_in) {
    if (seed_in >= 10000000) return seed_in;
    unsigned long t = (unsigned long)time(NULL);
    unsigned long c = (unsigned long)clock();
    return (int)(((t ^ c) % 80000000UL) + 10000000UL);
    }


    If you need a portable function across different platforms
    you may want to write an own random() function, code based
    on some simple, proven algorithm. Or borrow a piece of code
    from some existing public source code library.

    For "_replaying_ a round in a silly game" across platforms
    (or generally) you should not seed it with time() or other
    random factors (as shown in your code snippet).

    Janis

    I've been thinking about this very issue. There *must* be a
    portable default (hence time at least for now) if the user
    supplies a non-sensical seed or no seed at all & it also
    must be deterministic or the seed idea wont work.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu Dec 25 09:10:16 2025
    From Newsgroup: comp.lang.c

    On 2025-12-25 05:30, Michael Sanders wrote:

    Ok seed is working for replaying a game. =)
    Pure C, no ncurses etc, built in help too.

    How did you implement the colored squares? Using characters from
    an "extended" character set and ANSI controls for the colors?

    You know this game don't you Janis?
    Simple little project. Screenshot...

    <https://drive.google.com/file/d/1dKSjDzmu0mLy76GWrlZUT9HVK72GiP9d/view>

    Looks nice.[*]

    Only that it looks as if one gets too much information (compared
    to the original Mastermind)! IMO and AFAIK one should *not* get
    the _exact_ place of a wrong digit indicated. (The green hints
    should all be left aligned, and the [optional] red ones all right
    aligned, and the blue ones in between.)

    Janis

    [*] 18 months ago I've written an optically less appealing command
    line variant to refresh my Algol 68 skills. Though I just notice
    that I hadn't finish it yet; I've only implemented playing modes 1
    and 2. ;-)


    Enter a value for #places: 4
    Enter a value for #colors: 6

    Available playing modes:
    1 - computer selects, human guesses
    2 - human selects, computer guesses
    3 - alternate select/guess roles per game
    4 - human selects, human guesses
    5 - computer selects, computer guesses
    0 - leave the game
    Choose the playing mode (1-5): 1

    A secret color combination to guess has been chosen.
    You have to guess it.

    Enter a color combination: 1122
    Turn 1: 1 1 2 2 -
    Enter a color combination: 3345
    Turn 2: 3 3 4 5 - @
    Enter a color combination: 6444
    Turn 3: 6 4 4 4 - @@
    Enter a color combination: 6646
    Turn 4: 6 6 4 6 - @@@@

    You guessed it!

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu Dec 25 09:51:14 2025
    From Newsgroup: comp.lang.c

    On 2025-12-25 06:09, BGB wrote:
    On 12/24/2025 5:22 AM, BGB wrote:

    Some people seem to really like using lookup tables.

    Of course; they can speed up things significantly. And simplify
    the operations for contemporary data sizes (bytes, 64 bit words,
    etc.).


    I don't really get the point of lookup table driven RNGs.

    In e.g. bit-oriented algorithms (e.g. based on LFSR) you can speed
    up processing significantly by processing larger quantities (like
    octets/bytes) and processing/accessing values then byte-wise.[*]

    Someone already mentioned that a bit-wise operating PN-generator
    for random numbers could make use of such a table driven approach.
    (You can extend that principle to larger quantities than bits, e.g.
    to gain from larger processor word lengths, especially it you need
    those larger entities as result in the first place.)

    A lot of these ones are, say, a table with 256 spots, and an index.
    Each time one generates a random number (usually a byte), it returns the value at that location in the table and advances to the next index.

    Sometimes some get clever and use an algorithm to jitter the table index.

    I have mostly seen this strategy used in old game engines.

    These typically fail the image test as by design they give repeating patterns.

    [...]

    Janis

    [*] Here's some old "C" example for a CRC-16 using table-lookup... http://random.gridbug.de/ccitt_crc16.c

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Thu Dec 25 03:07:03 2025
    From Newsgroup: comp.lang.c

    On 12/24/2025 1:00 PM, Lawrence DrCOOliveiro wrote:
    On Wed, 24 Dec 2025 15:21:11 -0000 (UTC), Michael Sanders wrote:

    On Wed, 24 Dec 2025 06:16:51 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Wish there was such a 'device' under Windows...

    You should get one if you install WSL2.

    To be fair there is the 'Windows entropy pool' & its
    non-deterministic too but its only available via API.

    You begin to see why Microsoft is supporting Linux more and more.


    Usual strategy IME is usually to save a file or similar with RNG state
    for a big RNG hidden inside somewhere, and then the program loads the
    file, goes through an "entropy mining" step, and then either immediately
    or eventually saves the file again.

    This approach is generally portable.


    In some other types of programs, the RNG state is hidden inside some
    other type of data that tends to be saved and reloaded (such as the
    player state in a 3D engine).

    One entropy-mining process is to use "clock()" or similar and then spin
    in a loop for a certain amount of time effectively building a hash of
    the values returned by clock. The exact timing when the values change
    will tend to carry a certain amount of entropy.


    Say, for example:
    t0=clock();
    t0e=t0+(0.1*CLOCKS_PER_SEC); //usually not too obnoxious.
    t1=t0;
    seed1=1; seed2=1;
    while(t1<t0e)
    { t1=clock(); seed1+=t1; seed2+=seed1; }
    seed1+=((uint32_t)seed1)+(seed1>>32);
    seed2+=((uint32_t)seed2)+(seed2>>32);
    seed=seed1^seed2;

    This can be combined with the saved/restored RNG state.

    This entropy-mining approach seems to work reasonably well despite its limitations (will typically give a unique value each run).

    granted, doesn't work if the system as a whole is sufficiently
    deterministic (doesn't really work on microcontrollers or similar).

    ...


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Thu Dec 25 04:24:11 2025
    From Newsgroup: comp.lang.c

    On 12/25/2025 2:51 AM, Janis Papanagnou wrote:
    On 2025-12-25 06:09, BGB wrote:
    On 12/24/2025 5:22 AM, BGB wrote:

    Some people seem to really like using lookup tables.

    Of course; they can speed up things significantly. And simplify
    the operations for contemporary data sizes (bytes, 64 bit words,
    etc.).


    I don't really get the point of lookup table driven RNGs.

    In e.g. bit-oriented algorithms (e.g. based on LFSR) you can speed
    up processing significantly by processing larger quantities (like octets/bytes) and processing/accessing values then byte-wise.[*]

    Someone already mentioned that a bit-wise operating PN-generator
    for random numbers could make use of such a table driven approach.
    (You can extend that principle to larger quantities than bits, e.g.
    to gain from larger processor word lengths, especially it you need
    those larger entities as result in the first place.)



    I meant, more like this (from Doom):
    <==
    unsigned char rndtable[256] = {
    0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 ,
    74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 ,
    95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 ,
    52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 ,
    149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 ,
    145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 ,
    175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 ,
    25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 ,
    94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 ,
    136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 ,
    135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 ,
    80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 ,
    24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 ,
    145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 ,
    28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 ,
    71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 ,
    17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 ,
    197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 ,
    120, 163, 236, 249
    };

    int rndindex = 0;
    int prndindex = 0;

    int P_Random (void)
    {
    prndindex = (prndindex+1)&0xff;
    return rndtable[prndindex];
    }

    int M_Random (void)
    {
    rndindex = (rndindex+1)&0xff;
    return rndtable[rndindex];
    }

    void M_ClearRandom (void)
    {
    rndindex = prndindex = 0;
    }



    Like, why?...

    There are different/better ways of doing RNG.



    Granted, can't really change this in Doom, because modifying it means
    that demos will desync (or, at least, more than they desync already). Apparently some source-ports (like ZDoom) put significant effort into immitating the behavior of each version of the Doom engine such that the
    demos from the corresponding IWADs work (with magic to try to identify
    the Doom version by pattern-matching the IWAD).

    My port is based on linuxdoom, and doesn't go this far (there was a combination of both algorithmic differences and also Doom to some extent
    being sensitive to the contents of memory from out-of-bounds memory
    accesses).

    Had tried copying a few simple tricks from ZDoom, but were not enough to
    get desync free demo-playback (though, it plays out the same between
    builds for different targets, ...).



    Kind of a similar issue with my ROTT port, except that ROTT is more of a
    mess, and there isn't even consistency between runs, or even necessarily between different runs of the same demo within the demo loop.

    Had sort of got it stabilized, but in the past could seemingly only get consistent desync-free playback when built for 32-bit x86.


    But, then again, did at one point investigate why a feature had broken
    in ROTT, and realized that the code in question was sensitive to (among
    other things) the specific behavior of a signed-integer overflow when promoting to a larger type (it only worked correctly if it first
    overflowed at the narrower size and was then promoted; rather than one
    where it simply did the operation at the wider size with no
    wrap-on-overflow behavior).

    Well, and also a lot of cases where there was a lot of out-of-bounds
    accesses, which I then needed to try to hack around to handle in some
    way "sensible" that was still mostly consistent with the original out-of-bounds access behavior.

    And, some amount of "YOLO" array access, namely accessing one array out
    of bounds to access stuff in another array mostly depending on their
    relative locations in memory (this is not a coding style that I
    particularly consider acceptable, unless maybe the application itself
    directly controls the memory layout, rather than something that is
    mostly in the domain of the C compiler or linker).

    Though, there did still end up being a certain amount of "characteristic behavior" changes resulting from fixing up OOB issues.

    Say, for example, if the player manages to get outside the map, rather
    than the game corrupting memory and crashing; one may find themselves on
    an infinite plane where the map just wraps around (once every 128 blocks).

    But, despite everything, there is still some sort of lingering
    non-determinism in the mix here.

    ...


    Well, at least my Hexen port seemingly has non-desync'ing demos. And my Heretic port seemingly works correctly with the Shareware WAD I have
    (but not with a registered WAD; but the registered WAD has a lot more
    maps vs the Shareware version).

    As with Doom, behavior is mostly consistent across targets. Though, each
    did require modification to work on ARM, since ARM defaults to unsigned
    char and the various versions of the Doom engine had contained code that assumed that 'char' was signed (well, along with a lot of obligatory
    changes in "r_data.c" and similar to deal with 64-bit targets having
    64-bit pointers and similar, etc).

    ...



    Some later games (mostly starting with Quake) had instead avoided desync issues by recording game event messages. Rather than trying to record
    and playback player keyboard inputs and similar.

    Though, in my attempt to port Quake3, I ended up dropping the original
    QVM as it was designed in a way that wasn't really compatible with
    64-bit machines (but, the "game" code was also written in C and so could
    be compiled to native code).

    In my case, one possibility could be (if I want to move away from native
    code) compiling the "game VM" scripts as RISC-V or similar and then
    using an RV64 VM in place of the QVM (well, this or figure out how to
    make the QVM work on a 64-bit host, but this is harder as it also
    assumes sharing structs between the host and VM, which only really works
    if both have the same layout).

    Some fudging was needed with the Quake 1 "progs.dat" VM, but had been a
    little simpler as less was required here (went from absolute addresses
    to hunk-relative addresses for things like strings, etc). Well, because
    again the VM was designed in a way that it was not particularly
    amendable to changing memory layout for some structures based on things
    like pointer size. In this case, there was a relatively smaller cross
    section, so switching to functions to wrap/unwrap strings and similar
    was less of an issue.

    ...



    A lot of these ones are, say, a table with 256 spots, and an index.
    Each time one generates a random number (usually a byte), it returns
    the value at that location in the table and advances to the next index.

    Sometimes some get clever and use an algorithm to jitter the table index.

    I have mostly seen this strategy used in old game engines.

    These typically fail the image test as by design they give repeating
    patterns.

    [...]

    Janis

    [*] Here's some old "C" example for a CRC-16 using table-lookup... http://random.gridbug.de/ccitt_crc16.c


    Not exactly the same thing, but works.

    Even as such though, lookup table driven CRC still isn't particularly
    fast if compared with "linear sums of 32-bit words" or similar.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Thu Dec 25 19:31:26 2025
    From Newsgroup: comp.lang.c

    On Thu, 25 Dec 2025 03:07:03 -0600, BGB wrote:

    One entropy-mining process is to use "clock()" or similar and then
    spin in a loop for a certain amount of time effectively building a
    hash of the values returned by clock. The exact timing when the
    values change will tend to carry a certain amount of entropy.

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu Dec 25 21:14:00 2025
    From Newsgroup: comp.lang.c

    On 2025-12-25 20:31, Lawrence DrCOOliveiro wrote:

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.

    $ ln /dev/sda /dev/rnd # ;-)

    Too bad that I recently switched to SSDs. :-/

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Thu Dec 25 15:29:59 2025
    From Newsgroup: comp.lang.c

    On 12/25/2025 1:31 PM, Lawrence DrCOOliveiro wrote:
    On Thu, 25 Dec 2025 03:07:03 -0600, BGB wrote:

    One entropy-mining process is to use "clock()" or similar and then
    spin in a loop for a certain amount of time effectively building a
    hash of the values returned by clock. The exact timing when the
    values change will tend to carry a certain amount of entropy.

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.

    Yeah, but one doesn't easily have access to this information.
    Likewise to access from the low order bits of CPU thermometers or
    similar, etc.

    For some of my targets, there is also no HDD (typically, everything runs
    off of SD cards).


    FWIW, in my own CPU design, there is actually a hardware RNG where
    internal signals are basically gathered up and fed around the bus in a
    special noise channel and used to continuously feed into a hardware RNG
    for which a value can be read with a special CPU instruction.

    But, alas, mainline CPUs lack such a feature.

    On x86, it is also possible to get some level of entropy from mining
    RDTSC, but this is non-portable.




    But, yeah, tested out a few more RNG designs, and ATM:
    seed1 ^= ~(seed2>>47); seed2 ^= ~(seed1>>43); // 4 cycles
    seed1 ^= (seed1<<13); seed2 ^= (seed2>>11); // 4 cycles
    seed1 ^= (seed1>>19); seed2 ^= (seed2<<17); // 4 cycles
    val = ((seed1 ^ seed2) >> 32) & 0x7FFF; // 6 cycles
    Seems to be working pretty OK (decent randomness), and is moderately fast.

    Add cost of +4 cycles for LD (2c penalty), +2 ST
    Est cost: Around 24 clock cycles.


    Though, breaking up the shifts and xors using temporaries could be used
    to micro-optimize it a little more (vs trying to rely on compile-time instruction shuffling).

    Downside as that this particular approach (XOR'ing values with
    themselves and modifying the original variable before the next step),
    creates a lot of dependencies which limits the potential ILP (can't get
    ILP over 2 in this case).

    Where, the interleaved "seed1 = (seed1<<SH1) ^ (seed1>>SH2);" pattern
    allows for slightly higher ILP, but seemingly gets less randomness per
    shift (so the total dependency length ends up similar).

    In the CPU in question, would effectively need 4 seed values to get
    better ILP, say:
    seed1 ^= ~(seed2>>47); seed2 ^= ~(seed3>>43);
    seed3 ^= ~(seed4>>53); seed4 ^= ~(seed1>>41);
    // ~ 4 cycles
    seed1 ^= (seed1<<13); seed2 ^= (seed2>>11);
    seed3 ^= (seed3<< 7); seed4 ^= (seed4>> 5);
    seed1 ^= (seed1>>19); seed2 ^= (seed2<<17);
    seed3 ^= (seed3>>23); seed4 ^= (seed4<<29);
    // ~ 6 cycles
    val = ((seed1 ^ seed2) >> 32) & 0x7FFF; // 6 cycles
    //+ 8 cycles (Global LD/ST)
    Est Cost: Around 24 clock cycles.

    (24 cycle estimate assuming compiler shuffles instructions into a
    roughly optimal order).


    Though, with more operations still likely to be slower, despite the
    higher ILP potential (the higher ILP would merely reduce the slowdown
    from having more operations).

    In this case, ideally one wants to be able to have groups of 6
    non-dependent ALU operations on a 3-wide CPU with 2-cycle ALU latency.
    Though, in this case, achieving peak ILP would actually require ~ 6 or 8
    seed registers. Though, in this case, would require using XG3, for
    RISC-V it would also suffer due to RV64 only having 32 GPRs rather than
    64, so trying to use 8 seeds in parallel would run out of GPRs on RISC-V (don't want to spill, this will hurt a lot worse than the lost ILP).

    Where, if ILP is maximized, one can get to around 0.3 clock cycles per operation.



    But, on the target in question, 2 or 4 seeds is still likely to be considerably faster than, say:
    seed = seed * 0x0000FC4BA2F7ACABULL + 1;

    Which suffers from the "great evil" known as "slow 64-bit multiply"
    (needs 68 cycles to do a 64-bit multiply, similar to performing a divide).

    Where, the cost of the 64-bit integer multiply will stomp all over the
    cycle cost of the former (well, and not like "rand()" tends to be used
    all that heavily in any case).


    Well, contrast to the naive lookup-table approach:
    index=(index+1)&255; //latency = 8 cycles (3 LD, 2 ADD, 2 AND, 1 ST)
    val=table[index]; //latency = 5 cycles (2 ADD, 3 LD).
    So: ~ 13 cycles of latency.

    So, looks simple, but not great in this case (both weak, and latency
    isn't great for its weakness).


    Then again:
    seed = seed * 0x0000FC4BA2F7ACABULL + 1;
    //75 cycles: 3 LD, 1 const, 68 MUL, 2 ADD, 1 ST
    ret = (myseed >> 48) & 0x7FFF; //4 cycles: 2 SHR, 2 AND
    Cost: Around 80 cycles.


    ...


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul@nospam@needed.invalid to comp.lang.c on Thu Dec 25 23:25:39 2025
    From Newsgroup: comp.lang.c

    On Thu, 12/25/2025 4:29 PM, BGB wrote:
    On 12/25/2025 1:31 PM, Lawrence DrCOOliveiro wrote:
    On Thu, 25 Dec 2025 03:07:03 -0600, BGB wrote:

    One entropy-mining process is to use "clock()" or similar and then
    spin in a loop for a certain amount of time effectively building a
    hash of the values returned by clock. The exact timing when the
    values change will tend to carry a certain amount of entropy.

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.

    Yeah, but one doesn't easily have access to this information.
    Likewise to access from the low order bits of CPU thermometers or similar, etc.

    For some of my targets, there is also no HDD (typically, everything runs off of SD cards).


    FWIW, in my own CPU design, there is actually a hardware RNG where internal signals are basically gathered up and fed around the bus in a special noise channel and used to continuously feed into a hardware RNG for which a value can be read with a special CPU instruction.

    But, alas, mainline CPUs lack such a feature.

    How have you concluded such a thing ?

    My CPU happens to have a random number generator running at 500MB/sec.

    And it works on the same principle as other RNGs. It uses one physical
    process for entropy, and it uses a pseudo random number generator for
    the at-speed part (the 500MB/sec).

    On mine, there are 16 ring oscillators, with one ring oscillator
    having three inverters in a ring. The slowest oscillator has 59 inverters
    in a row. (The number of inverters must be an odd number, in order
    to ensure the oscillators start OK.) A sampling circuit samples all
    sixteen RO and creates a 16 bit number. The 16 bit number is a "seed" to
    the pseudo random number generator.

    Keywords like: "RDRAND, RDSEED"

    https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/white-papers/amd-random-number-generator.pdf

    There are other implementations. Intel has more than one method for doing this, historically.

    https://www.electronicdesign.com/resources/article/21796238/understanding-intels-ivy-bridge-random-number-generator

    The Linux people happen not to like those, but, they exist anyway, chugging away.

    And completely unrelated.

    https://www.2uo.de/myths-about-urandom/

    Paul
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Thu Dec 25 23:41:11 2025
    From Newsgroup: comp.lang.c

    On 12/25/2025 10:25 PM, Paul wrote:
    On Thu, 12/25/2025 4:29 PM, BGB wrote:
    On 12/25/2025 1:31 PM, Lawrence DrCOOliveiro wrote:
    On Thu, 25 Dec 2025 03:07:03 -0600, BGB wrote:

    One entropy-mining process is to use "clock()" or similar and then
    spin in a loop for a certain amount of time effectively building a
    hash of the values returned by clock. The exact timing when the
    values change will tend to carry a certain amount of entropy.

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.

    Yeah, but one doesn't easily have access to this information.
    Likewise to access from the low order bits of CPU thermometers or similar, etc.

    For some of my targets, there is also no HDD (typically, everything runs off of SD cards).


    FWIW, in my own CPU design, there is actually a hardware RNG where internal signals are basically gathered up and fed around the bus in a special noise channel and used to continuously feed into a hardware RNG for which a value can be read with a special CPU instruction.

    But, alas, mainline CPUs lack such a feature.

    How have you concluded such a thing ?

    My CPU happens to have a random number generator running at 500MB/sec.

    And it works on the same principle as other RNGs. It uses one physical process for entropy, and it uses a pseudo random number generator for
    the at-speed part (the 500MB/sec).

    On mine, there are 16 ring oscillators, with one ring oscillator
    having three inverters in a ring. The slowest oscillator has 59 inverters
    in a row. (The number of inverters must be an odd number, in order
    to ensure the oscillators start OK.) A sampling circuit samples all
    sixteen RO and creates a 16 bit number. The 16 bit number is a "seed" to
    the pseudo random number generator.

    Keywords like: "RDRAND, RDSEED"

    https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/white-papers/amd-random-number-generator.pdf

    There are other implementations. Intel has more than one method for doing this, historically.

    https://www.electronicdesign.com/resources/article/21796238/understanding-intels-ivy-bridge-random-number-generator

    The Linux people happen not to like those, but, they exist anyway, chugging away.

    And completely unrelated.

    https://www.2uo.de/myths-about-urandom/


    OK.

    I hadn't heard about it; seems my PC's CPU is just barely new enough to support it (apparently with a timing of around 1300 cycles per RDRAND on
    the Zen+).

    Would theoretically give around 2.85 million RNG's per second.

    Though, either way, relying on it wouldn't be particularly portable
    (would need something x86+compiler+CPU specific). Could maybe make sense
    for seeding a PRNG though if target-specific code is OK in this sense.



    Quick check:
    One of the RNG's algos mentioned previously runs at roughly 300 million
    RNGs in one second.

    One of the slightly faster designs pulls off around 800 million RNGs in
    one second.

    ...


    Though, one can maybe divide RNGs into categories:
    Needs to be true random;
    Pseudorandom is OK, but it doesn't matter;
    Needs to be deterministic.
    RNG only provides appearance of randomness,
    but said randomness needs to play out the same for each run.

    It is not "one size fits all" in this case...


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Fri Dec 26 05:42:18 2025
    From Newsgroup: comp.lang.c

    On Thu, 25 Dec 2025 23:25:39 -0500, Paul wrote:

    My CPU happens to have a random number generator running at
    500MB/sec.

    ...

    The Linux people happen not to like those, but, they exist anyway,
    chugging away.

    Because itrCOs difficult to see how you can trust them.

    Not without thoroughly mashing them through something like this <https://en.wikipedia.org/wiki/Fortuna_(PRNG)>, anyway.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul@nospam@needed.invalid to comp.lang.c on Fri Dec 26 01:52:15 2025
    From Newsgroup: comp.lang.c

    On Fri, 12/26/2025 12:42 AM, Lawrence DrCOOliveiro wrote:
    On Thu, 25 Dec 2025 23:25:39 -0500, Paul wrote:

    My CPU happens to have a random number generator running at
    500MB/sec.

    ...

    The Linux people happen not to like those, but, they exist anyway,
    chugging away.

    Because itrCOs difficult to see how you can trust them.

    Not without thoroughly mashing them through something like this <https://en.wikipedia.org/wiki/Fortuna_(PRNG)>, anyway.


    The claim was, that x86 processors didn't have anything.

    They do have something.

    And it has a source of entropy to do re-seed with.

    Not all the Intel ones have the same arch.

    And they're good enough for a "guess-a-number" game in C.

    If you want a conventional "repeatable-upon-request" sequence,
    then a regular PRNG can be used, and you can just make your
    own if you're bored. As long as it doesn't have zeros sensitivity
    (gets "stuck" in the all zeros state).

    A general rule of thumb, is "quality random numbers take time".
    Based on that, the 500MB/sec output I got from my processor,
    I wouldn't be using that for any serious purpose, without
    further study. I just thought it amusing that such a firehose
    existed on the processor, not that such a rate was safe to use.

    Paul



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Fri Dec 26 07:56:58 2025
    From Newsgroup: comp.lang.c

    On Fri, 26 Dec 2025 01:52:15 -0500, Paul wrote:

    On Fri, 12/26/2025 12:42 AM, Lawrence DrCOOliveiro wrote:

    On Thu, 25 Dec 2025 23:25:39 -0500, Paul wrote:

    My CPU happens to have a random number generator running at
    500MB/sec.

    ...

    The Linux people happen not to like those, but, they exist anyway,
    chugging away.

    Because itrCOs difficult to see how you can trust them.

    Not without thoroughly mashing them through something like this
    <https://en.wikipedia.org/wiki/Fortuna_(PRNG)>, anyway.

    The claim was, that x86 processors didn't have anything.

    Not what I was responding to. I said nothing about such a claim, either
    way.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Fri Dec 26 08:08:57 2025
    From Newsgroup: comp.lang.c

    On Thu, 25 Dec 2025 09:10:16 +0100, Janis Papanagnou wrote:

    How did you implement the colored squares? Using characters from
    an "extended" character set and ANSI controls for the colors?

    Yes exactly. I do have a fallback for older terminal emulators:

    void set_utf(void) {
    const char *env = getenv("MASTERMIND");
    UTF = (env && env[0] == '0' && env[1] == '\0') ? 0 : 1;
    }

    if (UTF) static const char *glyph_utf[] = { ... }; // unicode
    else static const char *glyph_xyz[] = { ... }; // ascii

    Check out <https://jrgraphix.net/r/Unicode/2500-257F>

    Lots of useful glyphs there. Assuming the reader's software can
    render these, here's some tree glyphs that provide a smart visual representation of branching hierarchical taxonomies. For instance,
    if the next number in the sequence is larger than its mother node,
    attach to that node, else climb the tree till the next mother node
    & seek attachment there...

    195, 510, 855, 1380, 25, 170

    195
    roLroC510
    roe rooroC855
    roe rooroC1380
    rooroC25
    rooroC170

    Very handy for sorting things given certain contexts.

    Looks nice.[*]

    Thanks

    Only that it looks as if one gets too much information (compared
    to the original Mastermind)! IMO and AFAIK one should *not* get
    the _exact_ place of a wrong digit indicated. (The green hints
    should all be left aligned, and the [optional] red ones all right
    aligned, and the blue ones in between.)

    Yes it's just a variant. I still need to merge/harmonize
    this new way (popular with some folks here in the U.S.)
    with the older 'proper' way you & I learned it.

    [*] 18 months ago I've written an optically less appealing command
    line variant to refresh my Algol 68 skills. Though I just notice
    that I hadn't finish it yet; I've only implemented playing modes 1
    and 2. ;-)


    Enter a value for #places: 4
    Enter a value for #colors: 6

    Available playing modes:
    1 - computer selects, human guesses
    2 - human selects, computer guesses
    3 - alternate select/guess roles per game
    4 - human selects, human guesses
    5 - computer selects, computer guesses
    0 - leave the game
    Choose the playing mode (1-5): 1

    A secret color combination to guess has been chosen.
    You have to guess it.

    Enter a color combination: 1122
    Turn 1: 1 1 2 2 -
    Enter a color combination: 3345
    Turn 2: 3 3 4 5 - @
    Enter a color combination: 6444
    Turn 3: 6 4 4 4 - @@
    Enter a color combination: 6646
    Turn 4: 6 6 4 6 - @@@@

    You guessed it!

    D*mn! Chuckle, that's 'god-mode' Janis, I could probably
    play 5 colors at say 6, no make that 7 chances, but after
    that, my brain begins to melt. =)
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Fri Dec 26 08:13:38 2025
    From Newsgroup: comp.lang.c

    On Thu, 25 Dec 2025 03:17:41 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Nothing specific to either cmd.exe or PowerShell, but the Windows
    command line is fundamentally broken. This is because it derives from
    the CP/M command line model, which in turn was inherited from old-time
    DEC operating systems.

    On these DEC systems, the command line was a simple string buffer. So
    there is this assumption that program invocation is always going to be mediated by some kind of rCLshellrCY program, and the concept of one
    program directly invoking another is either nonexistent, or only
    grudgingly tolerated.

    Contrast this with the Unix approach, where the command line is an
    array of separate string rCLwordsrCY. There is no rCLshellrCY that occupies a privileged place in the system; any program can directly invoke any
    other, without having to worry about properly escaping any special
    characters that might be (mis)interpreted by some rCLshellrCY.

    Bingo - you nailed it in my mind. To be sure, when in Windows you
    have to play by their rules as Michael S. more or less pointed out.

    Given a choice, I'm seeking stiller waters with the odd exception
    here & there. Its all good =)
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Fri Dec 26 08:23:56 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 23:35:55 -0600, BGB wrote:

    While arguably a typical C library "rand()" isn't that strong, if one
    has a number sequence of output random digits, it might still take an impractical amount of time to brute-force search the entire seed space
    for a 64-bit seed.

    That is a great point IMO. After reading this I used Gemini to get a guess
    on the number of permutations for A-Z, its reply was:

    'over 403 quintillion millions' of permatations for A-Z...

    Now if we split that list (assuming each line was randomized 26 characters
    A-Z) & used each line exactly *once*, then destroyed it, we might maintain
    some privacy. At least till quantum stuff is in every day use...
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Fri Dec 26 04:48:17 2025
    From Newsgroup: comp.lang.c

    On 12/26/2025 1:56 AM, Lawrence DrCOOliveiro wrote:
    On Fri, 26 Dec 2025 01:52:15 -0500, Paul wrote:

    On Fri, 12/26/2025 12:42 AM, Lawrence DrCOOliveiro wrote:

    On Thu, 25 Dec 2025 23:25:39 -0500, Paul wrote:

    My CPU happens to have a random number generator running at
    500MB/sec.

    ...

    The Linux people happen not to like those, but, they exist anyway,
    chugging away.

    Because itrCOs difficult to see how you can trust them.

    Not without thoroughly mashing them through something like this
    <https://en.wikipedia.org/wiki/Fortuna_(PRNG)>, anyway.

    The claim was, that x86 processors didn't have anything.

    Not what I was responding to. I said nothing about such a claim, either
    way.

    In my case, I was not aware of such a feature having been added, but I
    haven't really been keeping up with every new feature being added to x86
    in recent years.




    Some years ago, I got sorta burned on AVX, as it wasn't until comparably recently that I got a CPU that could actually run it (I tend to build
    PCs with parts that are a few generations behind, to keep cost more reasonable). And, it wasn't until very recently (a few months ago) that
    I got something where using AVX didn't make things actively slower (my
    main PC can run AVX, technically, but doing so performs poorly).

    Much after AVX, I sorta lost motivation to keep up on newer additions to
    the ISA; as often it would be a painfully long time before I would
    actually be able to use it (and I still don't have any PC's with CPUs
    made in this decade).


    Well, and then it has started seeming like in a longer term sense, x86
    may be doomed. Granted, its end has been predicted for a long time,
    though its main threat may be in part the end of Moore's law, which in a mostly steady-state it is likely that performance per area and
    performance per watt will become the dominant factors, and conventional
    x86 processors haven't been great on either front here (combined with a
    longer term timeframe making JIT compilation and eventually abandonment
    the more likely endgame).

    Well, and after Moore's law hits its limit, it may backslide slightly as things settle on whichever process node is most economical in a perf/$
    sense.

    Though, this is unlikely to happen quickly (more likely over a period of decades).

    Well, and then there are more near-term things, like the mess of things
    the whole "AI" thing is creating, and MS repeatedly shooting itself in
    the foot (maybe not enough to dethrone themselves, but they are pushing it).

    ...


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Fri Dec 26 14:48:45 2025
    From Newsgroup: comp.lang.c

    On 12/26/2025 2:23 AM, Michael Sanders wrote:
    On Wed, 24 Dec 2025 23:35:55 -0600, BGB wrote:

    While arguably a typical C library "rand()" isn't that strong, if one
    has a number sequence of output random digits, it might still take an
    impractical amount of time to brute-force search the entire seed space
    for a 64-bit seed.

    That is a great point IMO. After reading this I used Gemini to get a guess
    on the number of permutations for A-Z, its reply was:

    'over 403 quintillion millions' of permatations for A-Z...

    Now if we split that list (assuming each line was randomized 26 characters A-Z) & used each line exactly *once*, then destroyed it, we might maintain some privacy. At least till quantum stuff is in every day use...


    Though, to be fair, 26! is larger than 2^64.


    But, yeah, if you have 64 bits of entropy, while it can be brute forced,
    it wont happen very quickly (and most normal attackers aren't going to
    have a supercomputer or similar on-call to crack it faster).


    Contrast, say for something with a 32 seed, it could be possible that it
    could be brute forced within a few seconds or so.



    But, yeah, one of the main use-cases I had often had for large seed
    state RNGs is things like UUID generation, which requires a reasonably
    good RNG with 256 or preferably 512 bits or more of seed state.


    Intermediate is ASLR, where something like a 64 or 128-bit RNG is mostly
    fine. Well, and the effectiveness of ASLR is reduced by a few practical limitations:
    Can usually only do ASLR on page boundaries,
    losing some bits of entropy;
    May need to cluster ASLR into reused sections of the address space,
    as full randomization results in mostly-empty page table pages.

    Say, for example, you start with 48 bits,
    Cut off 12 to 16 bits for page offset,
    Cut off a few HOBs for userland (say 46b for userland space);
    Maybe quantize high order address into 256 or so buckets.
    ...


    Then one finds that effectively they have around 16 bits or so of
    entropy here.


    Well, say, for ASLR allocation, one might have a process like:
    For N tries, with a counter:
    Generate a random number to select a bucket;
    If index is an unused bucket try again (decrementing counter);
    Generate a random address within this bucket;
    Check if the span of pages is free;
    If so, allocate these pages, call it done.
    Else, retry (decrementing counter).
    For N more tries:
    Generate a full address over the allowed range;
    Check if pages are free;
    If so, allocate pages;
    Set up a bucket for this area of address space;
    Done.
    If we get here, likely the whole address space is full...
    Or the user is trying to allocate vast areas of address space,
    and fragmentation has won the day.

    The first step for allocation is to mark pages as reserved, then return
    the base address for the region. then generally the pages are modified
    to have the desired attributes. Typically (at least in my case) pages
    are not immediately assigned a backing location in the page-file, but
    this may happen lazily.

    On first access, if a read, it may be assigned initially to a read-only
    zero page (no dedicated backing RAM); on first write it is given backing
    RAM (and assigned a location in the pagefile). If something is paged
    out, it may detect if the page is all zeroes and potentially revert it
    back to an initial zeroed-page state (freeing the backing page rather
    than writing it to the pagefile). This can be used to reduce wasting RAM
    and pagefile space on bulk zeroed memory (can often be a significant
    chunk of RAM in some cases).

    Though, by itself has a risk of resulting in a situation where the
    pagefile is over-committed (there is more active memory allocated than available pagefile space should all this RAM actually be used). So,
    better to keep track of this and have allocations fail should the total
    number of committed pages would exceed the size of the pagefile
    regardless of whether or not all the pagefile pages are actually being
    used (well, could go the other way; but then there is a bad situation
    where backing storage runs out in the absence of new memory allocations, potentially resulting in processes crashing at random, rather than
    failing earlier by a "mmap()" call or similar returning NULL).



    Well, as can note my memory map (custom OS on a custom ISA, though I am
    also running RISC-V code on this to some extent) looks sorta like (47:32 only):
    0000: Low 4GB region (system, direct-mapped memory)
    0001..3FFF: Shared / Global Alloc region;
    4000..7FFF: Private Alloc (per-process private VAS)
    8000..BFFF: System Reserved (MMU)
    C000..FFFF: Hardware memory map stuff (NOMMU RAM and MMIO, etc)
    ...

    The idea here being that private address space is only visible to the
    process in question, but stuff in the global space may potentially be
    visible between processes (though is not necessarily accessible to all processes; idea is that an ACL based protection scheme would be used
    here rather than by address-space isolation).

    There is a region for direct-mapped RAM currently located in 0000_40000000..0000_7FFFFFFF; this is generally read-only from userland. Addresses in this range have virtual-address translation, but are mapped
    1:1 with backing RAM pages (so its size is naturally more limited).

    A sort of ASLR is used in the direct-mapping space as well, but less
    effective since the space is smaller. In this case, RNG tries to
    generate starting page addresses directly.

    Well, and, say (low 4GB):
    00xxxxxx: Mostly ROM, some SRAM for IRQs, and read-only niche pages.
    There are ROM pages whose sole purpose is to be all zeroes, etc.
    010xxxxx: RAM area, reserved for kernel stacks and similar
    011xxxxx: RAM area, kernel image starts here.
    ...


    Can note about how programs work in my case:
    The executable parts of an EXE or DLL are separate from the
    user-writable parts;
    Currently, the executable parts are held in direct-mapped memory rather
    than pagefile backed memory (stuff tended to break if these parts could
    be paged out);
    Things like writable memory (data/bss) and heap, are instead allocated
    in pagefile backed memory.


    Generally, the mappings for the EXEs and DLLs are shared between all
    instances of that binary; with data/bss being per-process (accessed via
    the GP/GBR register, *1).

    Well, except ELF PIE binaries, which need to load a new instance each
    time (so waste RAM, more so with the large amounts of clutter GCC leaves
    in the ELF binaries; where things like symbol tables, and worse, DWARF metadata, can be well in access of any actual code in the binary...).

    *1: In my ABI, GP/GBR always holds a pointer to a table which can be
    used by each image to reload its own data area. EXE's always have an
    index of 0 (since there is only ever one EXE per process instance) and
    each DLL is assigned an index number into the table at load time. By
    going through a ritual, functions can get back to their own table. This process is handled callee side, with functions deciding whether or not
    to do so based on criteria (may be potentially called from outside the
    current PE image, and which may access global variables and/or are
    non-leaf functions, etc).

    By default, my compiler tends to shuffle functions and variables around
    based on random numbers (different for each run of the compiler), though
    the effectiveness is reduced by also needing to try to pull related
    functions closer together, and commonly used variables closer to the
    start of the data section (commonly used globals may go into ".data"
    rather than ".bss" even if technically uninitialized; mostly to make it
    more efficient to access them with smaller displacements).

    But, the general goal here is to try to make it harder for any potential buffer-overflow shell-code to do anything useful (assuming it gets past
    the stack canaries, etc, which also use RNG values). There is a risk
    here that if a binary goes for long enough without being recompiled,
    that it could become more vulnerable (in this case, even in otherwise vulnerable code, there would be some security merit to periodic
    recompilation to shuffle around functions and generate new canary values).

    Though, had experimented with another mechanism to reduce the
    probability of shell-code, namely tagging function-pointers and
    link-register values to limit tampering (all the link registers and
    function pointers could be tagged with a magic number that is unique for
    each run).

    This sorta works, but is weakened if needing to deal with generic RISC-V
    code (use of AUIPC+ADDI is incompatible with this approach; and kinda
    defeated if any shell-code could simply use an untagged pointer; though
    a partial workaround is to trap if "JALR X0, 0(X1)" is used with an incorrectly tagged pointer, as this almost invariably means a
    return-address had been stomped; but would still be weak against
    stomping other values).

    Though one possibility could be to increase the strength of
    stack-canaries by flagging them with relocs, allowing the program loader
    to itself re-jitter the canary values without needing a recompile.


    Well, and GCC compiled code has an additional weakness here in that GCC doesn't normally use stack canaries (they seemingly do very little to
    give binaries any kind of resistance against buffer overflow exploits).


    Granted, none of these is an infallible defense, but if one piles on
    enough stuff, it becomes statistically improbable that any shell-code
    could make it past the defenses (any would-be attacker would likely need
    to know the RNG states at the moment a given process was created in
    order to craft an effective attack).

    Well, unless the attack is basically just to crash the process, this
    doesn't require knowing any of the RNG state.


    Well, one thing that isn't done in my case is to jitter the kernel load address, which could probably also make sense (always loading the kernel
    at the same address being itself a potential attack surface; say, if exploiting a kernel-mode buffer overflow or a breakdown in the memory-protection schemes, etc).


    ...


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From BGB@cr88192@gmail.com to comp.lang.c on Fri Dec 26 15:12:48 2025
    From Newsgroup: comp.lang.c

    On 12/26/2025 2:48 PM, BGB wrote:


    Though one possibility could be to increase the strength of stack-
    canaries by flagging them with relocs, allowing the program loader to
    itself re-jitter the canary values without needing a recompile.


    Well, and GCC compiled code has an additional weakness here in that GCC doesn't normally use stack canaries (they seemingly do very little to
    give binaries any kind of resistance against buffer overflow exploits).


    Groan... Self clarification:
    GCC does have an option to enable stack canaries, but it is opt-in
    rather than enabled by default (say, in MSVC one would need to opt-out instead).

    What I wrote could be misinterpreted, I meant GCC's default settings do
    little in the case of protecting against stack overflows (and was *not* implying that canaries were ineffective).

    But, alas, now people will probably be misinterpreting what I wrote and
    now I will need to deal with this, grr...



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Sun Dec 28 02:44:31 2025
    From Newsgroup: comp.lang.c

    On Wed, 24 Dec 2025 12:12:11 +0200
    Michael S <already5chosen@yahoo.com> wrote:
    On Wed, 24 Dec 2025 09:00:50 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:


    Also, the TestU01 suit is made for generators with 32-bit output.
    M. OrCONeill used ad hoc technique to make it applicable to
    generators with 64-bit output. Is this technique right? Or may be
    it put 64-bit PRNG at unfair disadvantage?

    My point of view is that generator can be used to generate long
    bistream. Then you can cut the bitstream and get number of
    desired size. Good tests should check that such usage leads
    to reasonable properties. So, fact that one generator produces
    32-bit pieces and other produces 64-bit pieces should be irrelevant
    to the test.


    What you say is correct in few use cases. But there are many uses
    cases (in field of testing of numeric code, probably, most of them)
    in which "less random" LS bits are acceptable.
    Not that I can see why it could be the case for MT19937-64, but it
    could apply to one of two of other 64-bit generators tested by
    O'Neill.


    Besides, I strongly disagree with at least one assertion made by OrCONeill: "While security-related applications should
    use a secure generator, because we cannot always know the future
    contexts in which our code will be used, it seems wise for all applications to avoid generators that make discovering their
    entire internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my application easy discovery of complete state of PRNG is not a
    defect.

    OrCONeill is not a prophet, ignore what she say it you think you
    know better (which is probably the above).

    Anyway, even if I am skeptical about her criticism of popular
    PRNGs, intuitively I agree with the constructive part of the
    article - medium-quality PRNG that feeds medium quality hash
    function can potentially produce very good fast PRNG with rather
    small internal state.

    She seem to care very much about having minimal possible state.
    That is may be nice on embeded systems, but in general I would
    happily accept slighty bigger state (say 256 bits). But if
    we can get good properties with very small state, then why not?
    After all looking at state and updating it takes code, so
    small state helps with having fast generator.


    Agreed.

    Concerning Mersenne Twister, she is not the only one to
    criticise it. My personal opinion is that given large
    state and not so simple update Mersenne Twister would
    have to be very very good to justify its use.

    One theoretical advantage of MT19937 is that it has period of
    astronomic proportions. Which means that one instance of PRNG could be de-multiplexed into millions or billions of sub-streams with no
    detectable degradation of the quality of each sub-stream.
    However I fail to see how de-multiplexing into more than ~ one
    thousand of sub-streams can be practical. And for the latter one does
    not need to be astronomical, something like period=2**96 would be
    fully sufficient with many bits to spare.
    So, in theory I agree with the criticism. But in practice I am not
    bothered by the size of MT state.

    But it
    fails some tests, so does not look _better_ than other
    generators.


    It would be interesting to find out what were those tests that failed.
    I wonder, if tests suit can run faster on multicore computer. I don't
    want to wait 5-6 hours just to find out that report does not provide
    an information that I am looking for.

    I reproduced results of M. O'Neil. Luckily, on semi-modern hardware
    (Coffee Lake or EPYC3) and for PRNGs in question BigCrash finishes in
    2-2.5 hours. Which is a pain, but considarably less so then 5 hours.
    mt19937 fails all tests of scomp_LinearComp() variaty (2 in Crash and 2
    in BigCrash). It passes all the rest of tests.
    After re-reading O'Neil, I see that she wrote that, but first time I
    didn't pay attention.
    I have read description of scomp_LinearComp(). I can't say that I
    understood much. Neither theory, nor parameters, in particular, even
    after looking though code I have no idea about the meaning of parameter
    r.
    I am not so sure that Pierre LrCOEcuyer himself fully understands this
    test, apart from the fact that many moons ago basic algorithm for
    calculation of linear complexity was published in IEEE Transactions on Information Theory. Otherwise, his description would not look so much
    as hand waving. As to O'Neil, she likely understands it better than
    myself, but that's not a huge achievement :(
    My less than scientific feeling about this test is that one part of it
    is looking if test is LFSR or LFSR-family and if the answer is yes then
    test fails.
    So, eccentrically, mt19937 is punished for what it is rather than for randomness of results that it produces.
    I made a minimal modification to mt19937 algorithm, telling it to skip
    every 19936th result word. With modification it easily passes all 3
    crash batteries of Pierre LrCOEcuyer.
    Do I think that my modified mt19937 is better than original? No, I
    don't. IMHO, the only thing it is better in is passing batteries of
    LrCOEcuyer.
    On related note, I think that even simple counter fed into high
    quality hash function (not cryptographically high quality, far
    less than that) can produce excellent PRNG with even smaller
    internal state. But not very fast one. Although the speed depends
    on specifics of used computer. I can imagine computer that has low-latency Rijndael128 instruction. On such computer, running
    counter through 3-4 rounds of Rijndael ill produce very good PRNG
    that is only 2-3 times slower than, for example, LCG 128/64.

    Maybe.


    May be I'd even test my hypothesis. Eventually. Except that, again, I
    am not thrilled by idea of waiting 6 hours for each result.

    I tested.
    It turned out that my hypothesis was wrong. Running counter through 3
    rounds of Rijndael128 is not enough. Running counter through 4 rounds
    is still not enough - it fails 1 test (#86) in BigCrash battery.
    I didn't test 5 rounds, but even if it is enough, which is likely, it
    would almost certainly be slower than other several known methods.
    All that with simple 64-bit binary counter as a state variable.
    With 128-bit state and with partial chaining of 64 bits of Rijndael
    output back into part of state (the other half of state is still a
    counter), passing all batteries appear very easy. It only takes one
    round for chaining and another one for hashing. But under O'Neil's
    figures of merit using 128-bit PRNG state considered cheating );
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Sun Dec 28 05:38:55 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> wrote:
    On Wed, 24 Dec 2025 12:12:11 +0200
    Michael S <already5chosen@yahoo.com> wrote:

    On Wed, 24 Dec 2025 09:00:50 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:


    Also, the TestU01 suit is made for generators with 32-bit output.
    M. OrCONeill used ad hoc technique to make it applicable to
    generators with 64-bit output. Is this technique right? Or may be
    it put 64-bit PRNG at unfair disadvantage?

    My point of view is that generator can be used to generate long
    bistream. Then you can cut the bitstream and get number of
    desired size. Good tests should check that such usage leads
    to reasonable properties. So, fact that one generator produces
    32-bit pieces and other produces 64-bit pieces should be irrelevant
    to the test.


    What you say is correct in few use cases. But there are many uses
    cases (in field of testing of numeric code, probably, most of them)
    in which "less random" LS bits are acceptable.
    Not that I can see why it could be the case for MT19937-64, but it
    could apply to one of two of other 64-bit generators tested by
    O'Neill.


    Besides, I strongly disagree with at least one assertion made by
    OrCONeill: "While security-related applications should
    use a secure generator, because we cannot always know the future
    contexts in which our code will be used, it seems wise for all
    applications to avoid generators that make discovering their
    entire internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a
    defect.

    OrCONeill is not a prophet, ignore what she say it you think you
    know better (which is probably the above).

    Anyway, even if I am skeptical about her criticism of popular
    PRNGs, intuitively I agree with the constructive part of the
    article - medium-quality PRNG that feeds medium quality hash
    function can potentially produce very good fast PRNG with rather
    small internal state.

    She seem to care very much about having minimal possible state.
    That is may be nice on embeded systems, but in general I would
    happily accept slighty bigger state (say 256 bits). But if
    we can get good properties with very small state, then why not?
    After all looking at state and updating it takes code, so
    small state helps with having fast generator.


    Agreed.

    Concerning Mersenne Twister, she is not the only one to
    criticise it. My personal opinion is that given large
    state and not so simple update Mersenne Twister would
    have to be very very good to justify its use.

    One theoretical advantage of MT19937 is that it has period of
    astronomic proportions. Which means that one instance of PRNG could be
    de-multiplexed into millions or billions of sub-streams with no
    detectable degradation of the quality of each sub-stream.
    However I fail to see how de-multiplexing into more than ~ one
    thousand of sub-streams can be practical. And for the latter one does
    not need to be astronomical, something like period=2**96 would be
    fully sufficient with many bits to spare.
    So, in theory I agree with the criticism. But in practice I am not
    bothered by the size of MT state.

    But it
    fails some tests, so does not look _better_ than other
    generators.


    It would be interesting to find out what were those tests that failed.
    I wonder, if tests suit can run faster on multicore computer. I don't
    want to wait 5-6 hours just to find out that report does not provide
    an information that I am looking for.


    I reproduced results of M. O'Neil. Luckily, on semi-modern hardware
    (Coffee Lake or EPYC3) and for PRNGs in question BigCrash finishes in
    2-2.5 hours. Which is a pain, but considarably less so then 5 hours.
    mt19937 fails all tests of scomp_LinearComp() variaty (2 in Crash and 2
    in BigCrash). It passes all the rest of tests.
    After re-reading O'Neil, I see that she wrote that, but first time I
    didn't pay attention.

    I have read description of scomp_LinearComp(). I can't say that I
    understood much. Neither theory, nor parameters, in particular, even
    after looking though code I have no idea about the meaning of parameter
    r.
    I am not so sure that Pierre LrCOEcuyer himself fully understands this
    test, apart from the fact that many moons ago basic algorithm for
    calculation of linear complexity was published in IEEE Transactions on Information Theory. Otherwise, his description would not look so much
    as hand waving. As to O'Neil, she likely understands it better than
    myself, but that's not a huge achievement :(
    My less than scientific feeling about this test is that one part of it
    is looking if test is LFSR or LFSR-family and if the answer is yes then
    test fails.
    So, eccentrically, mt19937 is punished for what it is rather than for randomness of results that it produces.

    Well, I peeked at the code but did not read destription of the
    test. In the code I see mention of Berlekamp-Massey. That is
    well-known algorithm to find regularites in data, basically
    tries to find linear recurence satisfied by the data. One of
    things that I do is looking for reccurences, including linear
    ones. Up to now I did not run any serious simulation for
    such things but I may wish to do so (and IIUC other people did
    run some simulations). When doing simulations I really do
    not want to see artifacts due to PRNG producing sequence with
    different features than random sequence. So for me, if mt19937
    produces sequence with visible extra linear regulatities that is
    significant failure.

    I made a minimal modification to mt19937 algorithm, telling it to skip
    every 19936th result word. With modification it easily passes all 3
    crash batteries of Pierre LrCOEcuyer.
    Do I think that my modified mt19937 is better than original? No, I
    don't. IMHO, the only thing it is better in is passing batteries of LrCOEcuyer.

    Your modification is enough to fool the tests. It is not clear
    to me if it is enough to fool better regularity finders, so
    probably the generator is still defective.

    Also, note basic priciple of statistial testing: you should collect
    and process data first, than apply _once_ statitical test. Repeated
    testing with tweaked data is likely to prodice false pass. If you
    really want to tweak data the whole thing should be treated as
    one composite test with its own acceptance criteria (which is more
    stringent than separate tests). Given that you used knowledge of
    failing tests to modify generator, passing test after that is much
    weaker claim than passing test for generator without knowledge of
    the tests.

    On related note, I think that even simple counter fed into high
    quality hash function (not cryptographically high quality, far
    less than that) can produce excellent PRNG with even smaller
    internal state. But not very fast one. Although the speed depends
    on specifics of used computer. I can imagine computer that has
    low-latency Rijndael128 instruction. On such computer, running
    counter through 3-4 rounds of Rijndael ill produce very good PRNG
    that is only 2-3 times slower than, for example, LCG 128/64.

    Maybe.


    May be I'd even test my hypothesis. Eventually. Except that, again, I
    am not thrilled by idea of waiting 6 hours for each result.


    I tested.
    It turned out that my hypothesis was wrong. Running counter through 3
    rounds of Rijndael128 is not enough. Running counter through 4 rounds
    is still not enough - it fails 1 test (#86) in BigCrash battery.
    I didn't test 5 rounds, but even if it is enough, which is likely, it
    would almost certainly be slower than other several known methods.

    All that with simple 64-bit binary counter as a state variable.

    With 128-bit state and with partial chaining of 64 bits of Rijndael
    output back into part of state (the other half of state is still a
    counter), passing all batteries appear very easy. It only takes one
    round for chaining and another one for hashing. But under O'Neil's
    figures of merit using 128-bit PRNG state considered cheating );

    O'Neil writes about birtday test: if you take values from N
    element set, with more than sqrt(N) samples you should get
    repetitions. Consider N equal to 2 to the power 64. In
    heavy use one could generate more than sqrt(N) values.
    In PRNG having 64-bit state and producing state as value
    all values are distinct, so generator would fail such a test.
    One could try to fix this by not exposing state, say producing
    only 32-bits in each step. But on 64-bit machine it looks
    more efficient to use 128-bit state and produce 64-bits in
    each step.
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Sun Dec 28 05:51:11 2025
    From Newsgroup: comp.lang.c

    On Thu, 25 Dec 2025 04:30:45 -0000 (UTC), Michael Sanders wrote:

    ... built in help too.

    Help is complete (err... except for man page)

    <https://drive.google.com/file/d/1m_9oOQX4KRrMVYpPobjfTGkC2UFSoiKk/view?usp=sharing>
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Sun Dec 28 12:35:33 2025
    From Newsgroup: comp.lang.c

    On Sun, 28 Dec 2025 05:38:55 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:
    Michael S <already5chosen@yahoo.com> wrote:
    On Wed, 24 Dec 2025 12:12:11 +0200
    Michael S <already5chosen@yahoo.com> wrote:

    On Wed, 24 Dec 2025 09:00:50 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:


    Also, the TestU01 suit is made for generators with 32-bit
    output. M. OrCONeill used ad hoc technique to make it applicable
    to generators with 64-bit output. Is this technique right? Or
    may be it put 64-bit PRNG at unfair disadvantage?

    My point of view is that generator can be used to generate long
    bistream. Then you can cut the bitstream and get number of
    desired size. Good tests should check that such usage leads
    to reasonable properties. So, fact that one generator produces
    32-bit pieces and other produces 64-bit pieces should be
    irrelevant to the test.


    What you say is correct in few use cases. But there are many uses
    cases (in field of testing of numeric code, probably, most of them)
    in which "less random" LS bits are acceptable.
    Not that I can see why it could be the case for MT19937-64, but it
    could apply to one of two of other 64-bit generators tested by
    O'Neill.


    Besides, I strongly disagree with at least one assertion made
    by OrCONeill: "While security-related applications should
    use a secure generator, because we cannot always know the
    future contexts in which our code will be used, it seems wise
    for all applications to avoid generators that make discovering
    their entire internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a
    defect.

    OrCONeill is not a prophet, ignore what she say it you think you
    know better (which is probably the above).

    Anyway, even if I am skeptical about her criticism of popular
    PRNGs, intuitively I agree with the constructive part of the
    article - medium-quality PRNG that feeds medium quality hash
    function can potentially produce very good fast PRNG with
    rather small internal state.

    She seem to care very much about having minimal possible state.
    That is may be nice on embeded systems, but in general I would
    happily accept slighty bigger state (say 256 bits). But if
    we can get good properties with very small state, then why not?
    After all looking at state and updating it takes code, so
    small state helps with having fast generator.


    Agreed.

    Concerning Mersenne Twister, she is not the only one to
    criticise it. My personal opinion is that given large
    state and not so simple update Mersenne Twister would
    have to be very very good to justify its use.

    One theoretical advantage of MT19937 is that it has period of
    astronomic proportions. Which means that one instance of PRNG
    could be de-multiplexed into millions or billions of sub-streams
    with no detectable degradation of the quality of each sub-stream.
    However I fail to see how de-multiplexing into more than ~ one
    thousand of sub-streams can be practical. And for the latter one
    does not need to be astronomical, something like period=2**96
    would be fully sufficient with many bits to spare.
    So, in theory I agree with the criticism. But in practice I am not
    bothered by the size of MT state.

    But it
    fails some tests, so does not look _better_ than other
    generators.


    It would be interesting to find out what were those tests that
    failed. I wonder, if tests suit can run faster on multicore
    computer. I don't want to wait 5-6 hours just to find out that
    report does not provide an information that I am looking for.


    I reproduced results of M. O'Neil. Luckily, on semi-modern hardware
    (Coffee Lake or EPYC3) and for PRNGs in question BigCrash finishes
    in 2-2.5 hours. Which is a pain, but considarably less so then 5
    hours. mt19937 fails all tests of scomp_LinearComp() variaty (2 in
    Crash and 2 in BigCrash). It passes all the rest of tests.
    After re-reading O'Neil, I see that she wrote that, but first time I
    didn't pay attention.

    I have read description of scomp_LinearComp(). I can't say that I understood much. Neither theory, nor parameters, in particular, even
    after looking though code I have no idea about the meaning of
    parameter r.
    I am not so sure that Pierre LrCOEcuyer himself fully understands this test, apart from the fact that many moons ago basic algorithm for calculation of linear complexity was published in IEEE Transactions
    on Information Theory. Otherwise, his description would not look so
    much as hand waving. As to O'Neil, she likely understands it better
    than myself, but that's not a huge achievement :(
    My less than scientific feeling about this test is that one part of
    it is looking if test is LFSR or LFSR-family and if the answer is
    yes then test fails.
    So, eccentrically, mt19937 is punished for what it is rather than
    for randomness of results that it produces.

    Well, I peeked at the code but did not read destription of the
    test. In the code I see mention of Berlekamp-Massey. That is
    well-known algorithm to find regularites in data, basically
    tries to find linear recurence satisfied by the data. One of
    things that I do is looking for reccurences, including linear
    ones. Up to now I did not run any serious simulation for
    such things but I may wish to do so (and IIUC other people did
    run some simulations). When doing simulations I really do
    not want to see artifacts due to PRNG producing sequence with
    different features than random sequence. So for me, if mt19937
    produces sequence with visible extra linear regulatities that is
    significant failure.

    I can't say much about Berlekamp-Massey algorithm in general. But this particular test (scomp_LinearComp) is looking for correlations between individual bits and is doing it at unusual intervals. In fact, it's
    even worth. It is looking not for correlations themselves but for
    regularity in changes of measure of correlation in a group of bits that
    is called linear complexity. And if it finds changes regular than it
    declares that PRNG is bad.
    I don't know, may be the test was not intentionally designed to punish
    long LSFRs that use relatively few bits at feedback step, but it
    behaves like the one.
    I personally don't care even if my PRNG has long-distance correlation
    between bits themselves, as long as the distance is long and not
    divisible by small integers. Much less so, when its not correlation
    itself, but a derivative of some abstract function of correlation.
    What I care about are numeric values of groups of bits taken at regular intervals, like 64 bits (most typical), 32 bits or rarely 8/16 bits.
    And for that MT appears to perform very well. For that I certainly
    trust it better than non-hashed output of LCG, esp. of LCG that has
    pow2 for modulo. And if said LCG happens to pass all LrCOEcuyer butteries
    it rather makes me more suspect of quality of batteries rather than less suspect of quality of this sort of LCG.
    Now, if somebody cares about non-correlativity of individual bits at
    strange intervals then, may be, LCG(mod 2**96) with output consisting
    of 32 MS bits of the state is what a doctor ordered. However I suspect
    that my usage of PRNG is not just different from his, it also happens to
    be far more common than his usage.
    I made a minimal modification to mt19937 algorithm, telling it to
    skip every 19936th result word. With modification it easily passes
    all 3 crash batteries of Pierre LrCOEcuyer.
    Do I think that my modified mt19937 is better than original? No, I
    don't. IMHO, the only thing it is better in is passing batteries of LrCOEcuyer.

    Your modification is enough to fool the tests. It is not clear
    to me if it is enough to fool better regularity finders, so
    probably the generator is still defective.

    That was my point. I manged to fool the test. Easily.
    But my conclusion is different. The test is defective, generator, both
    before and after modifications, is o.k..
    Also, note basic priciple of statistial testing: you should collect
    and process data first, than apply _once_ statitical test. Repeated
    testing with tweaked data is likely to prodice false pass. If you
    really want to tweak data the whole thing should be treated as
    one composite test with its own acceptance criteria (which is more
    stringent than separate tests). Given that you used knowledge of
    failing tests to modify generator, passing test after that is much
    weaker claim than passing test for generator without knowledge of
    the tests.

    Pay attention that specific skip modulo (19936) was chosen not
    because it's the only modulo that cause the test to pass. It was
    chosen as a biggest number < 19937 in order to make least noticeable
    impact on the speed of PRNG. Any smaller skip modulo will pass tests as
    well.
    So, if there was feedback loop on my part, it was not strong.
    And that, BTW, is my main criticism of methodology of Melissa O'Neil in constructive part of your article. She seems to apply a feedback loop
    by tweaking her suggested PCGs until they pass LrCOEcuyer butteries.
    On related note, I think that even simple counter fed into high
    quality hash function (not cryptographically high quality, far
    less than that) can produce excellent PRNG with even smaller
    internal state. But not very fast one. Although the speed
    depends on specifics of used computer. I can imagine computer
    that has low-latency Rijndael128 instruction. On such
    computer, running counter through 3-4 rounds of Rijndael ill
    produce very good PRNG that is only 2-3 times slower than, for
    example, LCG 128/64.

    Maybe.


    May be I'd even test my hypothesis. Eventually. Except that,
    again, I am not thrilled by idea of waiting 6 hours for each
    result.

    I tested.
    It turned out that my hypothesis was wrong. Running counter through
    3 rounds of Rijndael128 is not enough. Running counter through 4
    rounds is still not enough - it fails 1 test (#86) in BigCrash
    battery. I didn't test 5 rounds, but even if it is enough, which is
    likely, it would almost certainly be slower than other several
    known methods.

    All that with simple 64-bit binary counter as a state variable.

    With 128-bit state and with partial chaining of 64 bits of Rijndael
    output back into part of state (the other half of state is still a counter), passing all batteries appear very easy. It only takes one
    round for chaining and another one for hashing. But under O'Neil's
    figures of merit using 128-bit PRNG state considered cheating );

    O'Neil writes about birtday test: if you take values from N
    element set, with more than sqrt(N) samples you should get
    repetitions. Consider N equal to 2 to the power 64. In
    heavy use one could generate more than sqrt(N) values.
    In PRNG having 64-bit state and producing state as value
    all values are distinct, so generator would fail such a test.
    One could try to fix this by not exposing state, say producing
    only 32-bits in each step.
    Using 64-bit state and generating 32-bit output at each step is exactly
    what I did. In theory it should suffice to pass birthday tests.
    And indeed the test that failed was not one of the birthday tests, but svaria_WeightDistrib test (# 62 in BigCrash).
    But on 64-bit machine it looks
    more efficient to use 128-bit state and produce 64-bits in
    each step.

    It was not about practicality, but about testing the theory that even
    the worst possible long-periodic PRNG (and I can not think about
    anything worse as PRNG than simple counter) should be good enough when
    fed into decent hash as long as one is not greedy and (assuming
    2**64 period) one does not try to use more than 32 bits for output.
    I still think that the theory is correct, but I somewhat underestimated
    the required quality of the hash function.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Tue Dec 30 06:07:07 2025
    From Newsgroup: comp.lang.c

    On Fri, 26 Dec 2025 08:08:57 -0000 (UTC), Michael Sanders wrote:

    Yes it's just a variant. I still need to merge/harmonize
    this new way (popular with some folks here in the U.S.)
    with the older 'proper' way you & I learned it.

    Merged & harmonized...

    int main(int argc, char *argv[]) {

    // if app named 'moo' play bulls & cows else mastermind
    const char *p = strrchr(argv[0], '/');
    p = p ? p + 1 : argv[0];
    MOO = (strcmp(p, "moo") == 0) ? 1 : 0;

    int s = play(get_seed(argc, argv));
    fflush(stdout);
    fprintf(stderr, "%d\n", s);

    return 0;

    }
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Tue Dec 30 06:34:35 2025
    From Newsgroup: comp.lang.c

    On Fri, 26 Dec 2025 08:08:57 -0000 (UTC), Michael Sanders wrote:

    Check out <https://jrgraphix.net/r/Unicode/2500-257F>

    Lots of useful glyphs there. Assuming the reader's software can
    render these, here's some tree glyphs that provide a smart visual representation of branching hierarchical taxonomies.

    Another possibility is sixel graphics
    <https://www.arewesixelyet.com/>. Seems to be enjoying a mini-revival
    at the moment.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Tue Dec 30 14:05:19 2025
    From Newsgroup: comp.lang.c

    On Tue, 30 Dec 2025 06:34:35 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Another possibility is sixel graphics
    <https://www.arewesixelyet.com/>. Seems to be enjoying a mini-revival
    at the moment.

    Hey neato-burrito! Many thanks for the pointer Lawrence,
    appreciate that =)

    I was sort of dimly of aware of sixel somehow or another,
    but you've rattled me out of hibernation with this.

    Think I'll to try to cook up something I can play around with.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Tue Dec 30 18:42:30 2025
    From Newsgroup: comp.lang.c

    Michael Sanders <porkchop@invalid.foo> writes:
    On Fri, 26 Dec 2025 08:08:57 -0000 (UTC), Michael Sanders wrote:

    Yes it's just a variant. I still need to merge/harmonize
    this new way (popular with some folks here in the U.S.)
    with the older 'proper' way you & I learned it.

    Merged & harmonized...

    int main(int argc, char *argv[]) {

    // if app named 'moo' play bulls & cows else mastermind
    const char *p = strrchr(argv[0], '/');

    What if 'argv[0]' is NULL (and argc == 0)?

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 31 02:01:55 2025
    From Newsgroup: comp.lang.c

    On Tue, 30 Dec 2025 18:42:30 GMT, Scott Lurndal wrote:

    What if 'argv[0]' is NULL (and argc == 0)?

    Well, seems we have to make a choice, ISO vs. POSIX:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argcreA1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    int main(int argc, char *argv[]) {

    // if app named 'moo' play bulls & cows else mastermind
    const char *p = (argc > 0 && argv && argv[0]) ? argv[0] : "";
    const char *s = strrchr(p, '/');
    p = s ? s + 1 : p;
    MOO = (strcmp(p, "moo") == 0) ? 1 : 0;

    ...

    return 0;

    }

    *POSIX.1-2017 (and later)*

    POSIX execve() specification:

    The argument argv is an array of character pointers
    to null-terminated strings.

    The application shall ensure that argv[0] points to a filename
    string that is associated with the process being started.

    int main(int argc, char *argv[]) {

    // if app named 'moo' play bulls & cows else mastermind
    const char *p = strrchr(argv[0], '/');
    p = p ? p + 1 : argv[0];
    MOO = (strcmp(p, "moo") == 0) ? 1 : 0;

    ...

    return 0;

    }


    What say you?
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed Dec 31 03:10:52 2025
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argcreA1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 31 03:28:03 2025
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.

    Some windows snippets:

    int main(int argc, char **argv); // subsystem (text-mode)

    int wmain(int argc, wchar_t **argv); // gui

    And to muddy the waters further (what I tend to use)...

    #include <windows.h>
    #include <stdio.h>

    int main(void) {

    LPSTR cmd = GetCommandLineA();

    printf("Raw command line:\n%s\n", cmd);

    return 0;
    }

    app.exe foo "bar baz" 123
    "app.exe" foo "bar baz" 123
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Dec 30 19:35:12 2025
    From Newsgroup: comp.lang.c

    Michael Sanders <porkchop@invalid.foo> writes:
    On Tue, 30 Dec 2025 18:42:30 GMT, Scott Lurndal wrote:
    What if 'argv[0]' is NULL (and argc == 0)?

    Well, seems we have to make a choice, ISO vs. POSIX:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    [...]

    *POSIX.1-2017 (and later)*

    POSIX execve() specification:

    The argument argv is an array of character pointers
    to null-terminated strings.

    The application shall ensure that argv[0] points to a filename
    string that is associated with the process being started.

    [...]

    What say you?

    It happens that I recently spent some time looking into this.

    As you say, POSIX requires argc >= 1, but ISO C only guarantees
    argc >= 0.

    If argc == 0, a program that assumes argv[0] is non-null
    can run into serious problems if that assumption is invalid.
    In particular, a program called "pkexec" would try to traverse
    arguments starting with argv[1], which logically doesn't
    exist if argc==0. Due to the way program arguments are laid
    out in memory, argv[1] is also envp[0]. Frivolity ensued.
    See <https://nvd.nist.gov/vuln/detail/cve-2021-4034>.

    The Linux kernel updated execve to ensure that the invoked program
    has argc>=1. It was patched in early 2022. NetBSD still has this vulnerability.

    Summary: Some systems guarantee that argc>=1 and argv[0] points to
    a valid string, but software that's intended to be portable should
    tolerate argc==0 and argv[0]==NULL.

    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Dec 30 19:37:25 2025
    From Newsgroup: comp.lang.c

    Lawrence DrCOOliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argcreA1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1. I don't
    know whether Windows makes a similar guarantee, but it's entirely
    plausible that it could.

    But not all systems guarantee argc>=1, so any portable code shouldn't
    make assumptions beyond what's guaranteed by ISO C.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Wed Dec 31 04:51:11 2025
    From Newsgroup: comp.lang.c

    On Tue, 30 Dec 2025 19:35:12 -0800, Keith Thompson wrote:

    Summary: Some systems guarantee that argc>=1 and argv[0] points to
    a valid string, but software that's intended to be portable should
    tolerate argc==0 and argv[0]==NULL.

    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.

    Thanks Keith so I'll roll with ISO:

    int main(int argc, char *argv[]) {

    // ISO: if app named 'moo' play bulls & cows else mastermind
    const char *p = (argc > 0 && argv && argv[0]) ? argv[0] : "";
    const char *m = strrchr(p, '/');
    MOO = strcmp(m ? m + 1 : p, "moo") == 0;

    int s = get_seed(argc, argv); // returns default seed if not supplied

    fflush(stdout);
    fprintf(stderr, "%d\n", play(s)); // print seed to stderr

    return 0;
    }
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed Dec 31 09:37:08 2025
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 03:28:03 -0000 (UTC), Michael Sanders wrote:

    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.

    Some windows snippets:

    Are there any standards for how C argc/argv are supposed to behave on
    Windows?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Wed Dec 31 15:15:02 2025
    From Newsgroup: comp.lang.c

    On Tue, 30 Dec 2025 19:35:12 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Michael Sanders <porkchop@invalid.foo> writes:
    On Tue, 30 Dec 2025 18:42:30 GMT, Scott Lurndal wrote:
    What if 'argv[0]' is NULL (and argc == 0)?

    Well, seems we have to make a choice, ISO vs. POSIX:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    [...]

    *POSIX.1-2017 (and later)*

    POSIX execve() specification:

    The argument argv is an array of character pointers
    to null-terminated strings.

    The application shall ensure that argv[0] points to a filename
    string that is associated with the process being started.

    [...]

    What say you?

    It happens that I recently spent some time looking into this.

    As you say, POSIX requires argc >= 1, but ISO C only guarantees
    argc >= 0.

    If argc == 0, a program that assumes argv[0] is non-null
    can run into serious problems if that assumption is invalid.
    In particular, a program called "pkexec" would try to traverse
    arguments starting with argv[1], which logically doesn't
    exist if argc==0. Due to the way program arguments are laid
    out in memory, argv[1] is also envp[0]. Frivolity ensued.
    See <https://nvd.nist.gov/vuln/detail/cve-2021-4034>.

    The Linux kernel updated execve to ensure that the invoked program
    has argc>=1. It was patched in early 2022. NetBSD still has this vulnerability.

    Summary: Some systems guarantee that argc>=1 and argv[0] points to
    a valid string, but software that's intended to be portable should
    tolerate argc==0 and argv[0]==NULL.

    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.


    If you are interested in behavior on non-POSIX systems, primarily
    Windows, but possibly others as well (e.g. VMS) then using exec() in
    caller sounds like a bad idea. It just not how these systems work and
    not how people write programs on them.
    Even when exec() *appears* to works in some environments (like
    msys2) it likely emulated by spawn() followed by exit().

    I'd implement caller with spawn(). I suppose that even on POSIX it is
    more idiomatic.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Wed Dec 31 15:29:09 2025
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC)
    Lawrence DrCOOliveiro <ldo@nz.invalid> wrote:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0] through argv[argcreA1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.
    How did you come to this conclusion?
    Keith's test appears to show the opposite - he was not able to convince
    the Windows system to call application with empty argv list.
    Of course, he tried only one way out of many, but knowing how native
    Windows system call works, it appears extremely likely that on Windows
    argc < 1 is impossible. https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Wed Dec 31 17:24:29 2025
    From Newsgroup: comp.lang.c

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Lawrence DrCOOliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argcreA1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1.

    Does it? That seems to be up to the shell, since the exec()
    manual pages on the latest Fedora Core release don't indicate
    that argv[0] must be initialized or that argc be greater than zero.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul@nospam@needed.invalid to comp.lang.c on Wed Dec 31 12:30:02 2025
    From Newsgroup: comp.lang.c

    On Tue, 12/30/2025 10:37 PM, Keith Thompson wrote:
    Lawrence DrCOOliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argcreA1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1. I don't
    know whether Windows makes a similar guarantee, but it's entirely
    plausible that it could.

    But not all systems guarantee argc>=1, so any portable code shouldn't
    make assumptions beyond what's guaranteed by ISO C.


    The Windows guarantee is with MSVC, according to this answer.

    ********** CoPilot Question **********

    Does the Windows operating system guarantee that argc >= 1 for an EXE program ?

    argv[0] usually contains the program absolute path.

    ********** CoPilot Answer ************

    HererCOs the clear, standardsrCagrounded, WindowsrCaspecific answer yourCOre looking for rCo with citations from the search results.

    # **Short Answer**
    **Yes. On Windows, when using the Microsoft C runtime (MSVCRT), argc
    is always guaranteed to be reN 1**, and ** argv[0] always exists**.
    However, **Windows itself does *not* guarantee what argv[0] contains** -
    only that the C runtime *treats* it as the program name.

    ---

    # **What Windows Guarantees vs. What the C Runtime Guarantees**

    ### **Microsoft C Runtime Guarantee**
    MicrosoftrCOs documentation explicitly states:

    - *rCLSince the program name is considered an argument, the value of argc is at least one.rCY*

    So if your EXE is built with MSVC and uses the standard main or wmain , you can rely on:

    - argc >= 1
    - argv[0] is a valid nullrCaterminated string
    - argv[argc] == NULL

    ### **MSVC Startup Code Behavior**
    MSVC startup code parses the command line and
    **always inserts the program name as argv[0] ** (quoted if needed).

    ---

    # **What Windows *Does Not* Guarantee**
    Windows itself does **not** enforce that the first token of the command line
    is the executable name. This is a convention, not a rule:

    - * By convention the module name ... is the first argument ... Note that this is
    only a convention and is not enforced in any way. *

    This means:

    - A caller can technically pass *anything* as the first token.
    - A process created via CreateProcess can have an arbitrary lpCommandLine . - argv[0] may not be the absolute path.
    - It may not even be a valid path at all.

    This is why portable code should not assume argv[0] is the executable path.

    ---

    # **CrossrCaPlatform Note**
    The C standard does **not** guarantee that argv[0] contains the program name rCo only that it exists.
    This is echoed in the search results:

    - * In short: No... this is more of a common convention than a strict rule. *

    ---

    # **Final Summary**
    | Question | Answer |
    |------------------------------------------------------|------------------------------------------|
    | **Does Windows guarantee argc >= 1 ?** | **Yes rCo if using MSVCrCOs C runtime.** |
    | **Is argv[0] guaranteed to exist?** | **Yes rCo with MSVC.** |
    | **Is argv[0] guaranteed to be the absolute path?** | **No.** |
    | **Does Windows enforce any of this?** | **No rCo itrCOs the C runtime, not the OS.** |

    If you need the *actual* executable path, use:

    - GetModuleFileName(NULL, ...) (Windows API)

    ---

    https://learn.microsoft.com/en-us/cpp/c-language/argument-description?view=msvc-170&utm_source=copilot.com

    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments?view=msvc-170&utm_source=copilot.com

    https://github-wiki-see.page/m/johnstevenson/winbox-args/wiki/How-Windows-parses-the-command-line?utm_source=copilot.com

    https://www.codegenes.net/blog/is-argv-0-name-of-executable-an-accepted-standard-or-just-a-common-convention/?utm_source=copilot.com

    When argv[0] IsnrCOt the Executable Name

    4.1 Invocation via exec Functions
    4.2 Symbolic Links
    4.3 Shell Scripts and Aliases
    4.4 Debuggers, Emulators, and Special Environments

    ********** End CoPilot Answer ************

    Paul
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From bart@bc@freeuk.com to comp.lang.c on Wed Dec 31 18:42:45 2025
    From Newsgroup: comp.lang.c

    On 31/12/2025 17:30, Paul wrote:
    On Tue, 12/30/2025 10:37 PM, Keith Thompson wrote:
    Lawrence DrCOOliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argcreA1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1. I don't
    know whether Windows makes a similar guarantee, but it's entirely
    plausible that it could.

    But not all systems guarantee argc>=1, so any portable code shouldn't
    make assumptions beyond what's guaranteed by ISO C.


    The Windows guarantee is with MSVC, according to this answer.

    ********** CoPilot Question **********

    Does the Windows operating system guarantee that argc >= 1 for an EXE program ?

    argv[0] usually contains the program absolute path.

    ********** CoPilot Answer ************

    HererCOs the clear, standardsrCagrounded, WindowsrCaspecific answer yourCOre looking for rCo with citations from the search results.

    # **Short Answer**
    **Yes. On Windows, when using the Microsoft C runtime (MSVCRT), argc
    is always guaranteed to be reN 1**, and ** argv[0] always exists**. However, **Windows itself does *not* guarantee what argv[0] contains** -
    only that the C runtime *treats* it as the program name.

    ---

    # **What Windows Guarantees vs. What the C Runtime Guarantees**

    ### **Microsoft C Runtime Guarantee**
    MicrosoftrCOs documentation explicitly states:

    - *rCLSince the program name is considered an argument, the value of argc is at least one.rCY*

    So if your EXE is built with MSVC and uses the standard main or wmain , you can rely on:

    - argc >= 1
    - argv[0] is a valid nullrCaterminated string
    - argv[argc] == NULL

    ### **MSVC Startup Code Behavior**
    MSVC startup code parses the command line and
    **always inserts the program name as argv[0] ** (quoted if needed).

    ---

    # **What Windows *Does Not* Guarantee**
    Windows itself does **not** enforce that the first token of the command line is the executable name. This is a convention, not a rule:

    - * By convention the module name ... is the first argument ... Note that this is
    only a convention and is not enforced in any way. *

    This means:

    - A caller can technically pass *anything* as the first token.
    - A process created via CreateProcess can have an arbitrary lpCommandLine .
    - argv[0] may not be the absolute path.
    - It may not even be a valid path at all.

    This is why portable code should not assume argv[0] is the executable path.

    ---

    # **CrossrCaPlatform Note**
    The C standard does **not** guarantee that argv[0] contains the program name rCo only that it exists.
    This is echoed in the search results:

    - * In short: No... this is more of a common convention than a strict rule. *

    ---

    # **Final Summary**
    | Question | Answer |
    |------------------------------------------------------|------------------------------------------|
    | **Does Windows guarantee argc >= 1 ?** | **Yes rCo if using MSVCrCOs C runtime.** |
    | **Is argv[0] guaranteed to exist?** | **Yes rCo with MSVC.** |
    | **Is argv[0] guaranteed to be the absolute path?** | **No.** |
    | **Does Windows enforce any of this?** | **No rCo itrCOs the C runtime, not the OS.** |

    If you need the *actual* executable path, use:

    - GetModuleFileName(NULL, ...) (Windows API)

    ---

    https://learn.microsoft.com/en-us/cpp/c-language/argument-description?view=msvc-170&utm_source=copilot.com

    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments?view=msvc-170&utm_source=copilot.com

    https://github-wiki-see.page/m/johnstevenson/winbox-args/wiki/How-Windows-parses-the-command-line?utm_source=copilot.com

    https://www.codegenes.net/blog/is-argv-0-name-of-executable-an-accepted-standard-or-just-a-common-convention/?utm_source=copilot.com

    When argv[0] IsnrCOt the Executable Name

    4.1 Invocation via exec Functions
    4.2 Symbolic Links
    4.3 Shell Scripts and Aliases
    4.4 Debuggers, Emulators, and Special Environments

    ********** End CoPilot Answer ************

    So is that a Yes or No?

    My C compiler calls __getmainargs() in msvcrt.dll to get argc/argv.

    __getmainargs() is also imported by programs compiled with Tiny C, and
    also with gcc 14.x from winlibs.com. (I assume it is actually called for
    the same purpose.)

    The specs for __getmainargs() say that the returned argc value is always
    = 1.

    (I doubt whether msvcrt.dll, which is present because so many programs
    rely on it, is what is used by MSVC-compiled appls, but you'd have to
    look inside such an app to check. EXEs inside \windows\system tend to
    import DLLs with names like "api-ms-win...".)

    In any case, it is easy enough to do a check on argc's value in your applications. (And on Windows, if it is 0 and you really need the path,
    you can get it with GetModuleFileNameA().)
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul@nospam@needed.invalid to comp.lang.c on Wed Dec 31 15:07:02 2025
    From Newsgroup: comp.lang.c

    On Wed, 12/31/2025 1:42 PM, bart wrote:

    So is that a Yes or No?

    My C compiler calls __getmainargs() in msvcrt.dll to get argc/argv.

    __getmainargs() is also imported by programs compiled with Tiny C, and also with gcc 14.x from winlibs.com. (I assume it is actually called for the same purpose.)

    The specs for __getmainargs() say that the returned argc value is always >= 1.

    (I doubt whether msvcrt.dll, which is present because so many programs rely on it, is what is used by MSVC-compiled appls, but you'd have to look inside such an app to check. EXEs inside \windows\system tend to import DLLs with names like "api-ms-win...".)

    In any case, it is easy enough to do a check on argc's value in your applications. (And on Windows, if it is 0 and you really need the path, you can get it with GetModuleFileNameA().)

    The answer says "with MSVC". And your usage of msvcrt.dll likely applies
    to that statement.

    If you wrote your own version of a runtime msvcrt.dll then the answer could be no or maybe.

    I think if you're sufficiently clever, you can break that. But for
    the most part, lots of regular/lazy programming efforts will be guaranteeing
    a good result. I've used the argv[0] to determine the role of a program (encoder or decoder mode), and I'm just a very bad hobby programmer.

    Windows is pretty careless with metadata. In twenty years of watching Windows, I watched a program lose its name, and also one which had no parent. I thought when a program lost a parent that System() would own it. But when I checked, not even System was the parent. The parent was nothing. And Windows seemingly does not kill items malformed in that way. (The root cause may have been a RAM error, but I cannot be sure.)

    If you look in Task Manager in W10/W11, you cannot see Memory Compressor.
    If you use tasklist /V in a Terminal, then you can see it. That is just so you can contrast that with the behavior of Task Manager. And if you use Sysinternals (bought by Microsoft) Process Explorer utility, if you check Properties on Memory Compressor (even as administrator), no metadata is visible and the status says something about a necessary device is not responding.
    As if someone launched that in a particular way so it would be orphaned on purpose.

    But this is Windows for you. That's how you do things there.

    Paul


    Paul
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Wed Dec 31 22:18:30 2025
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 18:42:45 +0000
    bart <bc@freeuk.com> wrote:
    On 31/12/2025 17:30, Paul wrote:
    On Tue, 12/30/2025 10:37 PM, Keith Thompson wrote:
    Lawrence DrCOOliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members
    argv[0] through argv[argcreA1] inclusive shall contain pointers to
    strings which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains,
    so you shouldnrCOt be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1. I don't
    know whether Windows makes a similar guarantee, but it's entirely
    plausible that it could.

    But not all systems guarantee argc>=1, so any portable code
    shouldn't make assumptions beyond what's guaranteed by ISO C.


    The Windows guarantee is with MSVC, according to this answer.

    ********** CoPilot Question **********

    Does the Windows operating system guarantee that argc >= 1 for an
    EXE program ?

    argv[0] usually contains the program absolute path.

    ********** CoPilot Answer ************

    HererCOs the clear, standardsrCagrounded, WindowsrCaspecific answer yourCOre looking for rCo with citations from the search results.

    # **Short Answer**
    **Yes. On Windows, when using the Microsoft C runtime (MSVCRT), argc
    is always guaranteed to be reN 1**, and ** argv[0] always
    exists**. However, **Windows itself does *not* guarantee what
    argv[0] contains** - only that the C runtime *treats* it as the
    program name.

    ---

    # **What Windows Guarantees vs. What the C Runtime Guarantees**

    ### **Microsoft C Runtime Guarantee**
    MicrosoftrCOs documentation explicitly states:

    - *rCLSince the program name is considered an argument, the value of
    argc is at least one.rCY*

    So if your EXE is built with MSVC and uses the standard main or
    wmain , you can rely on:

    - argc >= 1
    - argv[0] is a valid nullrCaterminated string
    - argv[argc] == NULL

    ### **MSVC Startup Code Behavior**
    MSVC startup code parses the command line and
    **always inserts the program name as argv[0] ** (quoted if
    needed).

    ---

    # **What Windows *Does Not* Guarantee**
    Windows itself does **not** enforce that the first token of the
    command line is the executable name. This is a convention, not a
    rule:

    - * By convention the module name ... is the first argument ...
    Note that this is only a convention and is not enforced in any way.
    *

    This means:

    - A caller can technically pass *anything* as the first token.
    - A process created via CreateProcess can have an arbitrary
    lpCommandLine .
    - argv[0] may not be the absolute path.
    - It may not even be a valid path at all.

    This is why portable code should not assume argv[0] is the
    executable path.

    ---

    # **CrossrCaPlatform Note**
    The C standard does **not** guarantee that argv[0] contains the
    program name rCo only that it exists. This is echoed in the search
    results:

    - * In short: No... this is more of a common convention than a
    strict rule. *

    ---

    # **Final Summary**
    | Question | Answer
    | |------------------------------------------------------|------------------------------------------|
    | **Does Windows guarantee argc >= 1 ?** | **Yes rCo if
    using MSVCrCOs C runtime.** | | **Is argv[0] guaranteed to
    exist?** | **Yes rCo with MSVC.**
    | | **Is argv[0] guaranteed to be the absolute path?** | **No.**
    | | **Does Windows enforce any of
    this?** | **No rCo itrCOs the C runtime, not the OS.** |

    If you need the *actual* executable path, use:

    - GetModuleFileName(NULL, ...) (Windows API)

    ---

    https://learn.microsoft.com/en-us/cpp/c-language/argument-description?view=msvc-170&utm_source=copilot.com

    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments?view=msvc-170&utm_source=copilot.com

    https://github-wiki-see.page/m/johnstevenson/winbox-args/wiki/How-Windows-parses-the-command-line?utm_source=copilot.com

    https://www.codegenes.net/blog/is-argv-0-name-of-executable-an-accepted-standard-or-just-a-common-convention/?utm_source=copilot.com

    When argv[0] IsnrCOt the Executable Name

    4.1 Invocation via exec Functions
    4.2 Symbolic Links
    4.3 Shell Scripts and Aliases
    4.4 Debuggers, Emulators, and Special Environments

    ********** End CoPilot Answer ************

    So is that a Yes or No?

    My C compiler calls __getmainargs() in msvcrt.dll to get argc/argv.

    __getmainargs() is also imported by programs compiled with Tiny C,
    and also with gcc 14.x from winlibs.com. (I assume it is actually
    called for the same purpose.)

    The specs for __getmainargs() say that the returned argc value is
    always
    = 1.

    (I doubt whether msvcrt.dll, which is present because so many
    programs rely on it, is what is used by MSVC-compiled appls, but
    you'd have to look inside such an app to check. EXEs inside
    \windows\system tend to import DLLs with names like "api-ms-win...".)

    In any case, it is easy enough to do a check on argc's value in your applications. (And on Windows, if it is 0 and you really need the
    path, you can get it with GetModuleFileNameA().)
    I experimented a little with CreateProcess() in caller (parent) and GetCommandLine() in callee (child). It seems that [under Windows] it is impossible to pass empty command line to child process.
    It makes no difference whether lpCommandLine argument of CreateProcess()
    call is NULL or "". In both cases the OS fills command line of the child
    with copy of lpApplicationName parameter.
    What *is* possible is passing of command line parameter that contains
    one or several white spaces. How compiler/start up code interprets such
    command line depends on specific compiler/start up code.
    If, like in your case, start up code is borrowed from Microsoft, then
    such command line is interpreted as (argc = 1; argv[0] = ""; argv[1] =
    NULL). I would guess that any start up code that is based on CommandLineToArgvW() syscall will deliver the same result.
    Of course, there are other possible choices as well. As mentioned up
    thread, as far as C Standard is concerned, argc=0 is also legal. But
    not using Microsoft's utilities sounds like plenty of extra work for non-obvious gain.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed Dec 31 20:51:41 2025
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 15:15:02 +0200, Michael S wrote:

    ... using exec() in caller sounds like a bad idea. It just not how
    these systems work and not how people write programs on them.

    I donrCOt think IrCOve used exec() on its own without fork() much, but it
    is quite common to do fork() without exec(). ThatrCOs another thing that
    I suspect non-*nix systems donrCOt handle very well.

    I'd implement caller with spawn(). I suppose that even on POSIX it
    is more idiomatic.

    Do you mean posix_spawn(3)?
    <https://manpages.debian.org/posix_spawn(3)>
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed Dec 31 20:52:59 2025
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 15:29:09 +0200, Michael S wrote:

    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC)
    Lawrence DrCOOliveiro <ldo@nz.invalid> wrote:

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.

    How did you come to this conclusion?

    The fact that the C spec says so. Is there any standard on Windows for
    how different C compilers are supposed to handle argc/argv?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Wed Dec 31 20:55:59 2025
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 18:42:45 +0000, bart wrote:

    In any case, it is easy enough to do a check on argc's value in your applications. (And on Windows, if it is 0 and you really need the
    path, you can get it with GetModuleFileNameA().)

    Remember that, on *nix systems, the contents of argv are arbitrary and caller-specified. And none of them need bear any relation to the
    actual filename of the invoked executable.

    In fact, it is quite common for utilities to behave differently based
    on the name, as passed in argv[0], by which they are invoked.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From bart@bc@freeuk.com to comp.lang.c on Wed Dec 31 22:57:55 2025
    From Newsgroup: comp.lang.c

    On 31/12/2025 20:55, Lawrence DrCOOliveiro wrote:
    On Wed, 31 Dec 2025 18:42:45 +0000, bart wrote:

    In any case, it is easy enough to do a check on argc's value in your
    applications. (And on Windows, if it is 0 and you really need the
    path, you can get it with GetModuleFileNameA().)

    Remember that, on *nix systems, the contents of argv are arbitrary and caller-specified. And none of them need bear any relation to the
    actual filename of the invoked executable.

    In fact, it is quite common for utilities to behave differently based
    on the name, as passed in argv[0], by which they are invoked.

    Yes, I do that all the time (especially from my other languages that
    also make use of msvcrt.dll).

    But, there is a difference between argv[0] and GetModuleFileName().

    The latter returns the full path of the executable (which also then
    allows you to pick up associated files in the same folder).

    argv[0] merely returns what was typed on the command line to invoke the application.

    So if someone types:

    C:\abc> prog

    it may run a prog.exe found in, say, c:\programs\myapp, and return the
    full path as "c:\programs\myapp\prog.exe".

    args[0] will give you only "prog"; good luck with that!

    I found out a few years ago that this useful funcion doesn't exist on Unix-like systems. You have to do it a more complicated way that may or
    may not work.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Dec 31 15:00:24 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:
    On Tue, 30 Dec 2025 19:35:12 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    [...]
    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.

    If you are interested in behavior on non-POSIX systems, primarily
    Windows, but possibly others as well (e.g. VMS) then using exec() in
    caller sounds like a bad idea. It just not how these systems work and
    not how people write programs on them.
    Even when exec() *appears* to works in some environments (like
    msys2) it likely emulated by spawn() followed by exit().

    I'd implement caller with spawn(). I suppose that even on POSIX it is
    more idiomatic.

    If I were going to look into the behavior on Windows, I'd probably
    want to use Windows native features. (I tried my test on Cygwin,
    and the callee wasn't invoked.)

    Apparently the Windows way to invoke a program is CreateProcessA().
    But it takes the command line as a single string. There might not
    be a Windows-native way to exercise the kind of control over argc
    and argv provided by POSIX execve().
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Dec 31 15:03:34 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC)
    Lawrence DrCOOliveiro <ldo@nz.invalid> wrote:
    [...]
    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.

    How did you come to this conclusion?
    Keith's test appears to show the opposite - he was not able to convince
    the Windows system to call application with empty argv list.
    Of course, he tried only one way out of many, but knowing how native
    Windows system call works, it appears extremely likely that on Windows
    argc < 1 is impossible.

    To be clear, my test didn't show anything about Windows. I ran the test
    under Cygwin, not under native Windows.

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Dec 31 15:14:27 2025
    From Newsgroup: comp.lang.c

    Lawrence DrCOOliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 15:29:09 +0200, Michael S wrote:
    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC)
    Lawrence DrCOOliveiro <ldo@nz.invalid> wrote:
    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldnrCOt be relying on it.

    How did you come to this conclusion?

    The fact that the C spec says so.

    You may well be correct, but I don't know how you reached that
    conclusion.

    Older Linux kernels make it possible to invoke a program with argc==0
    and argv[0]==NULL. Newer Linux kernels have a modified execve() that guarantees argc==1 and argv[0]!=NULL. NetBSD still permits argc==0.

    So some Unix-like systems (try to) guarantee argc>0, and some do not.

    It's entirely possible that Windows goes beyond the ISO C
    requirements and explicitly or implicitly guarantees argc>0.
    It's also entirely possible that it doesn't. Do you have any
    concrete information one way or the other

    Is there any standard on Windows for
    how different C compilers are supposed to handle argc/argv?

    That's a good question, and I don't know the answer.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Dec 31 15:17:22 2025
    From Newsgroup: comp.lang.c

    scott@slp53.sl.home (Scott Lurndal) writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    [...]
    That's not clear. Linux (since 2022) guarantees argc>=1.

    Does it? That seems to be up to the shell, since the exec()
    manual pages on the latest Fedora Core release don't indicate
    that argv[0] must be initialized or that argc be greater than zero.

    As I understand it, all the exec() functions go through the execve()
    system call. In 2022, the Linux kernel execve() implementation was
    modified to guarantee that argc>0 and argv[0]!=NULL in the invoked
    program. A shell presumably can't bypass that.

    commit dcd46d897adb70d63e025f175a00a89797d31a43
    Author: Kees Cook <kees@kernel.org>
    Date: 2022-01-31 16:09:47 -0800

    exec: Force single empty string when argv is empty
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Dec 31 15:29:15 2025
    From Newsgroup: comp.lang.c

    Paul <nospam@needed.invalid> writes:
    [...]
    # **CrossrCaPlatform Note**
    The C standard does **not** guarantee that argv[0] contains the
    program name rCo only that it exists.
    [...]

    That isn't quite correct, or is at least misleading. ISO C guarantees
    that argv[0] exists, but not that it points to a string. On some
    systems, it can contain be a null pointer.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From highcrew@high.crew3868@fastmail.com to comp.lang.c on Thu Jan 1 00:31:32 2026
    From Newsgroup: comp.lang.c

    Hi,

    On 1/1/26 12:29 AM, Keith Thompson wrote:
    Paul <nospam@needed.invalid> writes:
    That isn't quite correct, or is at least misleading. ISO C guarantees
    that argv[0] exists, but not that it points to a string. On some
    systems, it can contain be a null pointer.

    I heard of this before.

    Is it just theoretical, or do we have actual systems where
    argv[0]==NULL? I never saw it happen in any modern operating system.
    --
    High Crew
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu Jan 1 01:45:48 2026
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 15:00:24 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:
    On Tue, 30 Dec 2025 19:35:12 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    [...]
    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.

    If you are interested in behavior on non-POSIX systems, primarily
    Windows, but possibly others as well (e.g. VMS) then using exec()
    in caller sounds like a bad idea. It just not how these systems
    work and not how people write programs on them.
    Even when exec() *appears* to works in some environments (like
    msys2) it likely emulated by spawn() followed by exit().

    I'd implement caller with spawn(). I suppose that even on POSIX it
    is more idiomatic.

    If I were going to look into the behavior on Windows, I'd probably
    want to use Windows native features. (I tried my test on Cygwin,
    and the callee wasn't invoked.)


    That's likely because under Windows callee is named callee.exe.
    I didn't try on cygwin, but that was the reason of failure under msys2.
    Also, I am not sure if slash in the name is allowed. May be, backslash
    is required.

    Apparently the Windows way to invoke a program is CreateProcessA().
    But it takes the command line as a single string. There might not
    be a Windows-native way to exercise the kind of control over argc
    and argv provided by POSIX execve().


    Yes, some tricky combinations easily possible under POSIX are hard to
    achieve under Windows. The result could depend on exact implementation
    of parsing of command line in C start up code. May be, some combinations
    are even impossible, although right now I can not think out an
    example.

    But that was not my point. My point was that analog of exec() family
    does not exist in Windows (or VMS). Even if it appears to exist that
    just means that your environment somehow fooling you. The PID of child
    is not the same as parent's.
    So, if you want cross-platform test, it makes more sense to use spawn()
    family. It does not look like your test will be any more complicated
    with spawn() and as a free benefit you can print some info of
    interest in the parent process after after spawn() call.

    However I was wrong in my suggestion. I didn't realize that spawn
    functions under Windows and their POSIX equivalents have different names
    and different order of arguments.
    So, I take my suggestion back.






    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Dec 31 16:00:45 2025
    From Newsgroup: comp.lang.c

    bart <bc@freeuk.com> writes:
    [...]
    argv[0] merely returns what was typed on the command line to invoke the application.

    So if someone types:

    C:\abc> prog

    it may run a prog.exe found in, say, c:\programs\myapp, and return the full path as
    "c:\programs\myapp\prog.exe".

    args[0] will give you only "prog"; good luck with that!
    [...]

    That's typically how it works, but it's not guaranteed.

    If a program is invoked from a shell on a Unix-like system, the
    shell will use something like fork() and one of the exec() functions,
    and will typically (perhaps always?) arrange for the new process's
    argv[0] to point to a copy of the program name given on the shell
    command line. (Programs that behave differently depending on the
    string pointed to by argv[0] are typically invoked via symbolic
    links, so for example vi and view might be names for the same
    executable.)

    But there are a number of other ways to invoke programs. In the
    example I posted, a C program calls execve() directly, and sets up
    argc and argv in a way that a shell would never do.

    For safety, any program should safely handle being invoked
    with unusual arguments -- especially any program with extra
    privileges. For simplicity, it could simply abort if argc==0
    and/or argv[0]==NULL.

    Similar considerations probably apply on Windows, though it seems that
    Windows tries to guarantee argc>1 and argv[0]!=NULL.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Dec 31 16:05:19 2025
    From Newsgroup: comp.lang.c

    highcrew <high.crew3868@fastmail.com> writes:
    On 1/1/26 12:29 AM, Keith Thompson wrote:
    Paul <nospam@needed.invalid> writes:
    That isn't quite correct, or is at least misleading. ISO C guarantees
    that argv[0] exists, but not that it points to a string. On some
    systems, it can contain be a null pointer.

    I heard of this before.

    Is it just theoretical, or do we have actual systems where
    argv[0]==NULL? I never saw it happen in any modern operating system.

    Some systems (try to) guarantee that argv[0] points to a string, which
    may or may not be the name of the program. But there are ways, on some systems, to invoke a program with argc==0 and argv[0]==NULL.

    For details see here (I've recently made some updates).

    https://github.com/Keith-S-Thompson/argv0
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Dec 31 16:34:44 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 31 Dec 2025 15:00:24 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    [...]
    If I were going to look into the behavior on Windows, I'd probably
    want to use Windows native features. (I tried my test on Cygwin,
    and the callee wasn't invoked.)

    That's likely because under Windows callee is named callee.exe.
    I didn't try on cygwin, but that was the reason of failure under msys2.
    Also, I am not sure if slash in the name is allowed. May be, backslash
    is required.

    No, that's definitely not it. Cygwin emulates a Unix-like system
    on top of Windows. It creates executables with a ".exe" suffix,
    but plays some tricks so that "foo.exe" also looks like "foo".
    Also, Cygwin uses "/" as a directory separator. When I modified
    the caller program to set argv[0], it worked correctly.

    I've updated the caller to show the result of execve() (which
    normally doesn't return). It returns -1 to indicate an error and
    sets errno to 7 (E2BIG, Argument list too long) -- which is a bit
    odd, but there's no E2SMALL code.

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Thu Jan 1 01:03:16 2026
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 22:57:55 +0000, bart wrote:

    But, there is a difference between argv[0] and GetModuleFileName().

    So the latter cannot be used as a simple substitute for the former, as
    you might have led us to believe.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Mike Terry@news.dead.person.stones@darjeeling.plus.com to comp.lang.c on Thu Jan 1 02:01:29 2026
    From Newsgroup: comp.lang.c

    On 31/12/2025 23:00, Keith Thompson wrote:
    Michael S <already5chosen@yahoo.com> writes:
    On Tue, 30 Dec 2025 19:35:12 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    [...]
    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.

    If you are interested in behavior on non-POSIX systems, primarily
    Windows, but possibly others as well (e.g. VMS) then using exec() in
    caller sounds like a bad idea. It just not how these systems work and
    not how people write programs on them.
    Even when exec() *appears* to works in some environments (like
    msys2) it likely emulated by spawn() followed by exit().

    I'd implement caller with spawn(). I suppose that even on POSIX it is
    more idiomatic.

    If I were going to look into the behavior on Windows, I'd probably
    want to use Windows native features. (I tried my test on Cygwin,
    and the callee wasn't invoked.)

    Apparently the Windows way to invoke a program is CreateProcessA().
    But it takes the command line as a single string. There might not
    be a Windows-native way to exercise the kind of control over argc
    and argv provided by POSIX execve().

    On Windows there is a command-line provided to applications via GetCommandLine() api. This is a
    single zero-terminated string. There is no mention of the possibility of GetCommandLine returning
    NULL.

    Windows (and in general non-*nix OS's) does not have a concept of processes having a list of strings
    as their basic start up mechanism. [Windows processes have an environment strings block, similar to
    *nix and used in the same way, but the "command line" is just a string...]

    Windows views parsing of the command-line string to be in the application domain, I guess. The MSVC
    compiler provides console-mode applications with the usual (argc, argv) but the MSVC application
    start-up code does this rather than the OS. For GUI-mode apps, MSVC provides a different interface
    to the user entry point. (Of course, if the app wants the command-line it can call the Windows api
    to get it, and if it wants command-line tokens it can parse it itself, or call CommandLineToArgv()
    api which is part of the shell api and replicates MSVC startup behaviour...)


    Mike.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Thu Jan 1 02:29:59 2026
    From Newsgroup: comp.lang.c

    On Thu, 1 Jan 2026 02:01:29 +0000, Mike Terry wrote:

    On Windows there is a command-line provided to applications via GetCommandLine() api. This is a single zero-terminated string.

    This idea comes from CP/M, which in turn copied it from the old DEC
    operating systems. This is a very primitive model, which never
    envisioned the more advanced kinds of things a Unix-style command line
    makes possible.

    Windows views parsing of the command-line string to be in the
    application domain, I guess.

    Consider a basic thing like quoting words on the command line, to
    allow the passing of arguments with spaces and other odd characters in
    them: with a raw command string, both ends have to agree on the same conventions for doing such things. Getting this wrong leads to great
    sadness.

    On *nix systems, there is no need for such conventions: the argv array
    will be passed literally from invoking program to invoking one, with
    no need for special quoting at all.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Thu Jan 1 07:23:06 2026
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 16:34:44 -0800, Keith Thompson wrote:

    [...]

    It creates executables with a ".exe" suffix,
    but plays some tricks so that "foo.exe" also looks like "foo".

    Small aside... Minus the suffix, path in order of search
    is (if i recall correctly):

    1. an associated 'opener' if any (can be defined by associating
    suffix .foo with notepad.exe or whatever, also inherits the
    calling app's icon)

    2. in memory 'alias' via script/batch (set var=foo)

    3. current folder

    4. system path

    but... this also depends on %COMSPEC% ($SHELL in Windows) typically:

    powershell.exe
    cmd.exe
    command.exe
    user defined (like perl.exe for .pl or "busybox sh" for .sh)
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Thu Jan 1 07:32:34 2026
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 09:37:08 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Are there any standards for how C argc/argv are supposed to behave on Windows?

    Good question, some more ways to open things (that I know of),
    see 2nd example for 'sort of' argc/argv...

    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <shellapi.h>

    int open_app(const wchar_t *exe_or_path) {
    HINSTANCE r = ShellExecuteW(NULL, L"open", exe_or_path, NULL, NULL, SW_SHOWNORMAL);
    return ((INT_PTR)r > 32) ? 0 : -1;
    }

    open_app(L"notepad.exe");
    open_app(L"C:\\Windows\\System32\\calc.exe");

    or...

    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <wchar.h>

    int launch_app(const wchar_t *cmdline) {
    STARTUPINFOW si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof si);
    ZeroMemory(&pi, sizeof pi);
    si.cb = sizeof si;

    /* CreateProcess *may modify the buffer */
    wchar_t buf[1024];
    wcsncpy(buf, cmdline, 1023);
    buf[1023] = L'\0';

    if (!CreateProcessW(
    NULL, // application name (NULL = parse from cmdline)
    buf, // command line (MUTABLE) <--
    NULL, NULL, // process/thread security
    FALSE, // inherit handles
    0, // creation flags
    NULL, // environment
    NULL, // working directory
    &si,
    &pi))
    return -1;

    /* fire-and-forget */
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
    }

    launch_app(L"notepad.exe C:\\temp\\notes.txt");
    launch_app(L\"\"C:\\Program Files\\VideoLAN\\VLC\\vlc.exe\" --fullscreen video.mp4\");
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From bart@bc@freeuk.com to comp.lang.c on Thu Jan 1 14:05:29 2026
    From Newsgroup: comp.lang.c

    On 01/01/2026 01:03, Lawrence DrCOOliveiro wrote:
    On Wed, 31 Dec 2025 22:57:55 +0000, bart wrote:

    But, there is a difference between argv[0] and GetModuleFileName().

    So the latter cannot be used as a simple substitute for the former, as
    you might have led us to believe.


    It depends on your needs. If you need to know exactly what was typed,
    then you use GetCommandLine and extract the first part of it.

    Or you just use __getmainargs. Or argv[0] if available.

    However, I was responding to this:

    LD'O:
    In fact, it is quite common for utilities to behave differently based
    on the name, as passed in argv[0], by which they are invoked.

    And previously I'd said this:

    (And on Windows, if it is 0 and you really need the path, you can get
    it with GetModuleFileNameA().)

    At that point I'd forgotten that GetModuleFileName gives you more
    information than argv[0], so you'd to extract it. But you'd have to do
    that anyway to clean up the input: somebody may have typed c:\abc\.\.\.\.\.\ProG

    So I wasn't misleading anybody.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Thu Jan 1 19:02:49 2026
    From Newsgroup: comp.lang.c

    On Thu, 1 Jan 2026 07:32:34 -0000 (UTC), Michael Sanders wrote:

    On Wed, 31 Dec 2025 09:37:08 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Are there any standards for how C argc/argv are supposed to behave
    on Windows?

    Good question, some more ways to open things (that I know of), see
    2nd example for 'sort of' argc/argv...

    [examples omitted]

    All those are at the sending end. But what would C code see at the
    receiving end?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Thu Jan 1 19:03:47 2026
    From Newsgroup: comp.lang.c

    On Thu, 1 Jan 2026 14:05:29 +0000, bart wrote:

    On 01/01/2026 01:03, Lawrence DrCOOliveiro wrote:

    On Wed, 31 Dec 2025 22:57:55 +0000, bart wrote:

    But, there is a difference between argv[0] and
    GetModuleFileName().

    So the latter cannot be used as a simple substitute for the former,
    as you might have led us to believe.

    It depends on your needs.

    You neglected to mention that when offering the substitute before
    though, didnrCOt you?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Thu Jan 1 19:20:08 2026
    From Newsgroup: comp.lang.c

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    All those are at the sending end. But what would C code see at the
    receiving end?

    Opp, you're right. My bad (stuck function pointer h*ll at the moment...)
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu Jan 1 21:53:20 2026
    From Newsgroup: comp.lang.c

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence DrCOOliveiro <ldo@nz.invalid> wrote:
    On Thu, 1 Jan 2026 07:32:34 -0000 (UTC), Michael Sanders wrote:

    On Wed, 31 Dec 2025 09:37:08 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Are there any standards for how C argc/argv are supposed to behave
    on Windows?

    Good question, some more ways to open things (that I know of), see
    2nd example for 'sort of' argc/argv...

    [examples omitted]

    All those are at the sending end. But what would C code see at the
    receiving end?
    The first three cases look very simple.
    1.
    argc=1
    argv[0]=notepad.exe
    argv[1]=NULL
    2.
    argc=1
    argv[0]=C:\Windows\System32\calc.exe
    argv[1]=NULL
    3.
    argc=2
    argv[0]=notepad.exe
    argv[1]=C:\temp\notes.txt
    argv[2]=NULL
    4th case does not look like a legal C code.
    It probably was screwed either by poster's news reader or by my
    news reader.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From bart@bc@freeuk.com to comp.lang.c on Thu Jan 1 20:28:52 2026
    From Newsgroup: comp.lang.c

    On 01/01/2026 19:03, Lawrence DrCOOliveiro wrote:
    On Thu, 1 Jan 2026 14:05:29 +0000, bart wrote:

    On 01/01/2026 01:03, Lawrence DrCOOliveiro wrote:

    On Wed, 31 Dec 2025 22:57:55 +0000, bart wrote:

    But, there is a difference between argv[0] and
    GetModuleFileName().

    So the latter cannot be used as a simple substitute for the former,
    as you might have led us to believe.

    It depends on your needs.

    You neglected to mention that when offering the substitute before
    though, didnrCOt you?


    FFS, why are you always looking for some argument?

    Have the last word if you like. If it makes you happier, I will admit I
    was deliberately misleading and totally wrong.

    But about what, I don't know! FWIW, I first mentioned GetModuleFileName
    as a possibility when argv[0] was not available.

    So I now withdraw that suggestion. That means that if argv[0] isn't
    available, then you're fucked, since there are apparently no workarounds
    that would be 100% equivalent to what argv[0] would have provided.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Thu Jan 1 23:50:00 2026
    From Newsgroup: comp.lang.c

    On Thu, 1 Jan 2026 21:53:20 +0200, Michael S wrote:

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence DrCOOliveiro <ldo@nz.invalid> wrote:

    All those are at the sending end. But what would C code see at the
    receiving end?

    The first three cases look very simple.

    Is there some spec in Windows which describes how that works?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Fri Jan 2 14:32:07 2026
    From Newsgroup: comp.lang.c

    On Thu, 1 Jan 2026 23:50:00 -0000 (UTC)
    Lawrence DrCOOliveiro <ldo@nz.invalid> wrote:
    On Thu, 1 Jan 2026 21:53:20 +0200, Michael S wrote:

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence DrCOOliveiro <ldo@nz.invalid> wrote:

    All those are at the sending end. But what would C code see at the
    receiving end?

    The first three cases look very simple.

    Is there some spec in Windows which describes how that works?
    There is a spec that describes how that works in Microsoft's
    implementation. That implementation is available free of charge to
    other Windows compilers.
    If vendor of Windows 'C' compiler decided to implement different
    algorithm then nobody can stop him.
    Through the years you were told so, by different people, and shown
    the spec may be 100 times. But being the trolll you are, you continue
    to ask.
    Still, for the benefit of more sincere readers and also for myself, in
    order to have both pieces in one place: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments
    More interesting and meaningful question is how to do the reverse.
    I.e. how to convert an argv[] array into flat form in a way that
    guarantees that CommandLineToArgvW() parses it back into original form?
    Is it even possible in general case or there exist limitations
    (ignoring, for sake of brevity, 2*15-1 size limit)?
    Microsoft certainly has reverse conversion implemented, e.g. here: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/spawnv-wspawnv
    But I am not aware of command line serialization part available as a
    library call in isolation from process creation part.
    I binged around and googled around, but all I was able to find was the
    name of the function that performs the work: __acrt_pack_wide_command_line_and_environment
    I was not able to find the source code of the function.
    [O.T.]
    I am sure that 15, 10 or even 5 years ago Google would give me link to
    the source in a second. Or, may be, 5 years ago Google already
    wouldn't, but Bing still would.
    But today both search engines are hopelessly crippled with AI and do not
    appear to actually search the web. Instead, the try to guess the
    answer I likely want to hear.
    [/O.T.]
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Fri Jan 2 16:18:39 2026
    From Newsgroup: comp.lang.c

    On Fri, 2 Jan 2026 14:32:07 +0200
    Michael S <already5chosen@yahoo.com> wrote:


    More interesting and meaningful question is how to do the reverse.
    I.e. how to convert an argv[] array into flat form in a way that
    guarantees that CommandLineToArgvW() parses it back into original
    form? Is it even possible in general case or there exist limitations (ignoring, for sake of brevity, 2*15-1 size limit)?

    Microsoft certainly has reverse conversion implemented, e.g. here: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/spawnv-wspawnv


    Experimenting with _spawnv() shows that Microsoft made no effort in the direction of invertible serialization/de-serialization of argv[] lists.
    That is, as long as there are no double quotes, everything works as
    expected. But when there are double quotes in the original argv[] then
    more often than not they can't be passed exactly.

    An exact result of de-serialization in presence of unusually placed
    " characters depends on the version of the library.
    Very old versions, like those before UCRT or relatively new
    versions of UCRT, like one in VS2022, do not produce an exact
    inversion, but otherwise their results are reasonable. The version of
    the library supplied with UCRT variant of msys2 tools is much less
    reasonable. I'd rather characterize it as buggy.
    Frankly, I don't understand how it is possible. It leads me to believe
    that parsing routines in C RTL do not use system-provided parser and
    instead do parsing by themselves.



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Fri Jan 2 20:46:47 2026
    From Newsgroup: comp.lang.c

    On Fri, 2 Jan 2026 14:32:07 +0200, Michael S wrote:

    Through the years you were told so, by different people, and shown
    the spec may be 100 times. But being the trolll you are, you
    continue to ask.

    Still, for the benefit of more sincere readers and also for myself,
    in order to have both pieces in one place: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments

    That second one begins with these words in large letters:

    rCLMICROSOFT SPECIFICrCY

    (Remember I was asking for a spec applicable to Windows generally.)

    WhorCOs trolling now?
    WhorCOs trolling now?

    (To the tune of rCLWhorCOs sorry now?rCY etc)
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Fri Jan 2 20:52:59 2026
    From Newsgroup: comp.lang.c

    On Fri, 2 Jan 2026 16:18:39 +0200, Michael S wrote:

    Experimenting with _spawnv() shows that Microsoft made no effort in the direction of invertible serialization/de-serialization of argv[] lists.
    That is, as long as there are no double quotes, everything works as
    expected. But when there are double quotes in the original argv[] then
    more often than not they can't be passed exactly.

    And every now and then, this kind of fudging can progress beyond
    regular inflammation sadness into the full-fledged tumour of a Windows
    security hole <https://web.archive.org/web/20240711101329/https://list.waikato.ac.nz/archives/list/wlug@list.waikato.ac.nz/message/NVHWSZP7QF7QKYJO2UCHGZC4ADWSNNS7/>.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Mike Terry@news.dead.person.stones@darjeeling.plus.com to comp.lang.c on Sat Jan 3 04:08:40 2026
    From Newsgroup: comp.lang.c

    On 02/01/2026 12:32, Michael S wrote:
    On Thu, 1 Jan 2026 23:50:00 -0000 (UTC)
    Lawrence DAOliveiro <ldo@nz.invalid> wrote:

    On Thu, 1 Jan 2026 21:53:20 +0200, Michael S wrote:

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence DAOliveiro <ldo@nz.invalid> wrote:

    All those are at the sending end. But what would C code see at the
    receiving end?

    The first three cases look very simple.

    Is there some spec in Windows which describes how that works?

    There is a spec that describes how that works in Microsoft's
    implementation. That implementation is available free of charge to
    other Windows compilers.
    If vendor of Windows 'C' compiler decided to implement different
    algorithm then nobody can stop him.

    Through the years you were told so, by different people, and shown
    the spec may be 100 times. But being the trolll you are, you continue
    to ask.

    Still, for the benefit of more sincere readers and also for myself, in
    order to have both pieces in one place: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments


    In the long distant past I investigated how MSVC converts a command-line to its argc/argv input.
    There was an internal routine in the CRT startup code that did pretty much what we would expect, and
    I reversed engineered that for my code (or did I just copy the code? surely the former!). The MSVC
    code did not call CommandLineToArgvW in those days, but reading the description of that api it all
    sounds very familiar - the state flags for controlling "quoted/unquoted" text, even vs odd numbers
    of backslashes and all that.

    I didn't find it difficult to create command-line strings to call C programs, given what I wanted
    those programs to see as argv[n] with n>0. I think it was just a case of quoting all arguments,
    then applying quoting rules as docuemented for CommandLineToArgvW to handle nested quotes/backslashes.

    But I can see a sticky problem - the MSVC parsing for argv[0] was completely separate from thr main
    loop handling other arguments. The logic was considerably simplified, assuming that argv[0] was the
    path for the module being invoked. Since that is expected to be a valid file system path, the logic
    did not handle nested quotes etc.. I think the logic was just:

    - if 1st char is a DQUOTE, copy chars for argv[0] up to next DQUOTE or null terminator.
    (enclosing DQUOTE chars are not included)
    - else copy chars for argv[0] up to next whitespace or null terminator.
    (all chars are included, I think including DQUOTE should it occur)

    Given this, it would not be possible to create certain argv[0] strings containing quotes etc., and I
    understand that the likes of execve() allow that possibility. So I don't know what should happen
    for this case. E.g. I don't see there is a command-line that gives argv[0] the string "\" ". This
    was never a problem for me in practice.

    There would always be at least an argv[0] with this logic, so MSVC ensures argc>0 and argv[0] !=
    NULL. (Of course, MSVC is not "Windows". Various posters in this thread seem to be asking "what
    does /Windows/ do regarding argc/argv?" as though the OS is responsible for setting them.)


    More interesting and meaningful question is how to do the reverse.
    I.e. how to convert an argv[] array into flat form in a way that
    guarantees that CommandLineToArgvW() parses it back into original form?
    Is it even possible in general case or there exist limitations
    (ignoring, for sake of brevity, 2*15-1 size limit)?

    Yes, programmers need this if they need to create a process to invoke some utility program which
    will see particular argv parameters. Users are used to typing in command-lines as a string, e.g. at
    a console, so I suppose they don't normally need to think about the argv[] parsing; they can just
    build the required command-line and use that. (But it's a problem in the general case.)

    The argc/argv problem seemed easy enough in practice if we only need to handle the "real" arguments
    argv[n] with n>0. (Involving CMD.EXE introduced much worse complications, as you might imagine.
    But generally I always thought that MS wasn't really interested in /documenting/ how programmers
    should do things like this, in the same way they never bothered explaining exactly how CMD
    processing worked. Probably because it was forever changing!... Put another way, for many years
    they were really more focussed on admins clicking buttons in some GUI!)


    Microsoft certainly has reverse conversion implemented, e.g. here: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/spawnv-wspawnv

    But I am not aware of command line serialization part available as a
    library call in isolation from process creation part.

    I binged around and googled around, but all I was able to find was the
    name of the function that performs the work: __acrt_pack_wide_command_line_and_environment

    I was not able to find the source code of the function.

    [O.T.]
    I am sure that 15, 10 or even 5 years ago Google would give me link to
    the source in a second. Or, may be, 5 years ago Google already
    wouldn't, but Bing still would.
    But today both search engines are hopelessly crippled with AI and do not appear to actually search the web. Instead, the try to guess the
    answer I likely want to hear.
    [/O.T.]

    I hear you on that! Also [O.T.] I used to have access to most of Windows source code as part of
    some Windows source code sharing program, so could have found answers to this sort of question with
    minimal effort. (My employer met the criteria based on number of Windows servers/desktops deployed.
    There were various hoops you had to jump through with contracts being regularly (re)signed by
    company board level directors and the likes. Wonder if they still do that... Anyway I'm long
    retired now.)


    Mike.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.c on Sat Jan 3 04:39:39 2026
    From Newsgroup: comp.lang.c

    On Sat, 3 Jan 2026 04:08:40 +0000, Mike Terry wrote:

    The argc/argv problem seemed easy enough in practice if we only need
    to handle the "real" arguments argv[n] with n>0. (Involving CMD.EXE introduced much worse complications, as you might imagine. But
    generally I always thought that MS wasn't really interested in
    /documenting/ how programmers should do things like this, in the
    same way they never bothered explaining exactly how CMD processing
    worked. Probably because it was forever changing!... Put another
    way, for many years they were really more focussed on admins
    clicking buttons in some GUI!)

    After years -- decades -- of conditioning its users to be allergic to
    the command line, now suddenly the rise of Linux has made command
    lines cool again. Leaving Microsoft in an awkward position ...
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Sat Jan 3 14:24:12 2026
    From Newsgroup: comp.lang.c

    On Sat, 3 Jan 2026 04:39:39 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    After years -- decades -- of conditioning its users to be allergic to
    the command line, now suddenly the rise of Linux has made command
    lines cool again. Leaving Microsoft in an awkward position ...

    And telemetry... compulsory updates... ads all over the place...
    (even paid editions). I'd rather have a quite conversation with
    my OS[1] instead of an argument. Microsoft has gotten /very/
    predacious the last few years at least from my point of view.

    [1] CLI is good for that
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Sat Jan 3 20:38:42 2026
    From Newsgroup: comp.lang.c

    On Sat, 3 Jan 2026 04:08:40 +0000
    Mike Terry <news.dead.person.stones@darjeeling.plus.com> wrote:

    On 02/01/2026 12:32, Michael S wrote:
    On Thu, 1 Jan 2026 23:50:00 -0000 (UTC)
    Lawrence DAOliveiro <ldo@nz.invalid> wrote:

    On Thu, 1 Jan 2026 21:53:20 +0200, Michael S wrote:

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence DAOliveiro <ldo@nz.invalid> wrote:

    All those are at the sending end. But what would C code see at
    the receiving end?

    The first three cases look very simple.

    Is there some spec in Windows which describes how that works?

    There is a spec that describes how that works in Microsoft's implementation. That implementation is available free of charge to
    other Windows compilers.
    If vendor of Windows 'C' compiler decided to implement different
    algorithm then nobody can stop him.

    Through the years you were told so, by different people, and shown
    the spec may be 100 times. But being the trolll you are, you
    continue to ask.

    Still, for the benefit of more sincere readers and also for myself,
    in order to have both pieces in one place: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments


    In the long distant past I investigated how MSVC converts a
    command-line to its argc/argv input. There was an internal routine in
    the CRT startup code that did pretty much what we would expect, and I reversed engineered that for my code (or did I just copy the code?
    surely the former!). The MSVC code did not call CommandLineToArgvW
    in those days, but reading the description of that api it all sounds
    very familiar - the state flags for controlling "quoted/unquoted"
    text, even vs odd numbers of backslashes and all that.

    I didn't find it difficult to create command-line strings to call C
    programs, given what I wanted those programs to see as argv[n] with
    0. I think it was just a case of quoting all arguments, then
    applying quoting rules as docuemented for CommandLineToArgvW to
    handle nested quotes/backslashes.

    But I can see a sticky problem - the MSVC parsing for argv[0] was
    completely separate from thr main loop handling other arguments. The
    logic was considerably simplified, assuming that argv[0] was the path
    for the module being invoked. Since that is expected to be a valid
    file system path, the logic did not handle nested quotes etc.. I
    think the logic was just:

    - if 1st char is a DQUOTE, copy chars for argv[0] up to next DQUOTE
    or null terminator. (enclosing DQUOTE chars are not included)
    - else copy chars for argv[0] up to next whitespace or null
    terminator. (all chars are included, I think including DQUOTE should
    it occur)

    Given this, it would not be possible to create certain argv[0]
    strings containing quotes etc., and I understand that the likes of
    execve() allow that possibility. So I don't know what should happen
    for this case. E.g. I don't see there is a command-line that gives
    argv[0] the string "\" ". This was never a problem for me in
    practice.

    There would always be at least an argv[0] with this logic, so MSVC
    ensures argc>0 and argv[0] != NULL. (Of course, MSVC is not
    "Windows". Various posters in this thread seem to be asking "what
    does /Windows/ do regarding argc/argv?" as though the OS is
    responsible for setting them.)


    More interesting and meaningful question is how to do the reverse.
    I.e. how to convert an argv[] array into flat form in a way that
    guarantees that CommandLineToArgvW() parses it back into original
    form? Is it even possible in general case or there exist limitations (ignoring, for sake of brevity, 2*15-1 size limit)?

    Yes, programmers need this if they need to create a process to invoke
    some utility program which will see particular argv parameters.
    Users are used to typing in command-lines as a string, e.g. at a
    console, so I suppose they don't normally need to think about the
    argv[] parsing; they can just build the required command-line and use
    that. (But it's a problem in the general case.)


    I hoped that Microsoft's implementation of _spawnv() at least
    makes an effort at reverse transformation. But test shows that it
    does not. _spawnl() appears to do something very dumb - just concatenate
    all argv[] strings inserting one space character between parts.
    So, it has troubles not just with double quotes but even with spaces
    within arguments.


    The argc/argv problem seemed easy enough in practice if we only need
    to handle the "real" arguments argv[n] with n>0. (Involving CMD.EXE introduced much worse complications, as you might imagine. But
    generally I always thought that MS wasn't really interested in
    /documenting/ how programmers should do things like this, in the same
    way they never bothered explaining exactly how CMD processing worked.
    Probably because it was forever changing!... Put another way, for
    many years they were really more focussed on admins clicking buttons
    in some GUI!)


    To be fair, powershell exists for many more years than WinNt family was existing before emergence of powershell.
    The problem with it, at least IMHO, is a second system syndrome.
    command.com and to significant extent cmd.exe as well, seen as
    programming language was too weak and too primitive. With Powershell
    they went too far in the opposite direction - its rather powerfull, as programming language, but nott as simple at simple tasks as one expects
    from OS shell.
    So, considering that fewer and fewer people with programmer's attitude
    are employed as admins, it's no wonder that most of them are incapable
    to write powershell scripts by themselves. At best, they can cat&past.







    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Mon Jan 5 14:21:44 2026
    From Newsgroup: comp.lang.c

    I experimented a bit more (in fact, more like a lot more) with
    test batteries of LrCOEcuyer. It led me to conclusion that occasional
    failure in the either middle or big battery means nothing.
    Sometimes even cripto-quality PRNG does not pass one or another test.
    Then you try to reproduce it and see that with any other seed that you
    try a failure does not happen.
    All in all, it makes me more suspect of PRNGs that consistently pass
    both batteries with various seed. I start to see it as a sign of
    PRNG being rigged to pass tests.
    Said above does not apply to scomp_LinearComp() failures of mt19937.
    Those failures are very consistent. I just don't consider them
    significant for my own use of PRNGs or for any other uses of PRNG that
    I personally ever encountered.
    Overall an experience strengthened my position that general wisdom,
    previously shared by O'Neil herself, got it right: in absence of the
    special considerations people should select mt19937 and especially
    mt19937-64 as their default PRNGs of choice.
    Looking closer, apart from its properties of randomness and apart from
    huge period (which does not matter for me) I started to appreciate for
    mt19937 for the following properties that I was not aware before:
    - it does not use multiplier. So good fit for embedded systems that
    have no (or very slow) multiplier HW.
    - algorithm is very SIMD-friendly, so optimized implementations can be
    very very fast on modern x86 and ARM64 hardware.
    The latter property also means that very fast FPGA implementation is
    easily possible as long as designer is willing to through at it
    moderate amount of logic resources.
    Said above does not mean that PCG generators of O'Neil have no place. Intuitively, they appear not bad. But the theory is unproven, optimized implementation is likely slower that optimized mt19937, claimed
    "security" advantages are nonsense as admitted later by O'Neil herself.
    And, as said above, I no longer trust her empirical methodology, based
    on work of LrCOEcuyer.
    So, PCG generators are valuable addition to the toolbox, but not good
    enough to change my default.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Geoff@geoff@invalid.invalid to comp.lang.c on Mon Jan 5 20:00:26 2026
    From Newsgroup: comp.lang.c

    On Wed, 31 Dec 2025 15:14:27 -0800, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    It's entirely possible that Windows goes beyond the ISO C
    requirements and explicitly or implicitly guarantees argc>0.
    It's also entirely possible that it doesn't. Do you have any
    concrete information one way or the other

    https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Wed Jan 7 10:51:16 2026
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> wrote:
    I experimented a bit more (in fact, more like a lot more) with
    test batteries of LrCOEcuyer. It led me to conclusion that occasional
    failure in the either middle or big battery means nothing.
    Sometimes even cripto-quality PRNG does not pass one or another test.
    Then you try to reproduce it and see that with any other seed that you
    try a failure does not happen.
    All in all, it makes me more suspect of PRNGs that consistently pass
    both batteries with various seed. I start to see it as a sign of
    PRNG being rigged to pass tests.

    Well, that depends on the tests and threshhold in the tests.
    Some tests when fed with trurly random source will produce produce
    very small variation of the results. With generous threshhold
    such test will essentially never fail for trurly random source.
    OTOH when expected variation of the results is larger and
    threshhold is tight, then trurly random source will fail the
    test from time to time. And if you test long enough you should
    be able to estimate probability of failure and possibly compare
    is with theoretical result if available.

    To say the truth, I do not know what failures of crypto-quality PRNG
    means. It may mean that the test is of tight kind that is supposed
    to fail from time to time for trurly random source. Or it may mean
    to PRNG improves cypto part at cost of statistics. That is
    non-uniform distribution of the output is not a problem in
    crypto applications. Simply for crypto purposes future output
    should be not predictable from current and past output only.
    And slightly non-uniform distribution can increase probablity
    of test failure enough that you can observe such failures.

    BTW: You mentioned using counter and hardware AES128 round.
    Given cheap AES128 round I would try 128-bit state and AES128
    round as state update. I do not know if hardware AES128 is
    fast enough to make it wortwhile, but using AES128 round as a
    state update should be much better than scrambling a counter.

    Said above does not apply to scomp_LinearComp() failures of mt19937.
    Those failures are very consistent. I just don't consider them
    significant for my own use of PRNGs or for any other uses of PRNG that
    I personally ever encountered.

    Overall an experience strengthened my position that general wisdom, previously shared by O'Neil herself, got it right: in absence of the
    special considerations people should select mt19937 and especially
    mt19937-64 as their default PRNGs of choice.
    Looking closer, apart from its properties of randomness and apart from
    huge period

    Huge period alone is easy. AFAICS matrix variants of LCG can
    easily get quite large periods. I did not test matrix LCG,
    but on statistical tests they should be essentially as good
    as multiprecision LCG, but should be cheaper to implement.

    Just to be clear, I mean equation x_{n+1} = Ax_n + b, wher x_n
    is a vector reprezenting n-th state, A is a matrix and b is a
    vector. Matrix A may be somewhat sparse, that is have limited
    number of non-zero entries, and some entries my be simple, like
    1 or -1. With proper choice of a and b period should be
    comparable with number of availalable states.

    I see no reason to go for very long periods, already 512 bits
    of state allow perids which in practice should be indistingushable
    from infinite period.

    (which does not matter for me) I started to appreciate for
    mt19937 for the following properties that I was not aware before:
    - it does not use multiplier. So good fit for embedded systems that
    have no (or very slow) multiplier HW.

    Biggest MCU with no hardware multiplier that I have has 2kB RAM.
    I do not want mt19937 on such a machine. 64-bit multiplication
    on 8-biter with hardware multiplier may be slow, so 64-bit LCG
    (and improvements based on it) may be slow. But multiplication
    nicely spreads out bits, so it is not clear to me if there is
    equally good cheaper alternative. If needed I would investigate
    matrix LCG, they may be slightly cheaper.

    - algorithm is very SIMD-friendly, so optimized implementations can be
    very very fast on modern x86 and ARM64 hardware.

    Just size of the state puts limit how fast it can be. And size of
    the state means that it will compete for cache with user data.
    BTW: AFAICS matrix LCG can be SIMD friendly too.

    The latter property also means that very fast FPGA implementation is
    easily possible as long as designer is willing to through at it
    moderate amount of logic resources.

    Said above does not mean that PCG generators of O'Neil have no place. Intuitively, they appear not bad. But the theory is unproven, optimized implementation is likely slower that optimized mt19937, claimed
    "security" advantages are nonsense as admitted later by O'Neil herself.
    And, as said above, I no longer trust her empirical methodology, based
    on work of LrCOEcuyer.
    So, PCG generators are valuable addition to the toolbox, but not good
    enough to change my default.

    I agree that ATM it is not entirely clear if PCG-s are as good as
    suggested by tests. But I am surprised by your opinion about
    speed, I did not analyze deeply either of them, but PCG-s are way
    simpler, so I would expect them to be faster.
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jan 7 07:39:08 2026
    From Newsgroup: comp.lang.c

    John McCue <jmclnx@gmail.com.invalid> writes:

    Michael Sanders <porkchop@invalid.foo> wrote:

    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jan 7 07:46:40 2026
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:

    [regarding rand() and srand()]

    Pay attention that C Standard only requires for the same seed to always produces the same sequence. There is no requirement that different
    seeds have to produce different sequences.
    So, for generator in your example, implementation like below would be
    fully legal. Personally, I wouldn't even consider it as particularly
    poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    It seems better to do, for example,

    void srand(unsigned seed ) { init = seed - !seed;}

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jan 7 07:50:09 2026
    From Newsgroup: comp.lang.c

    antispam@fricas.org (Waldek Hebisch) writes:

    [...]
    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. O?Neill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.

    Interesting paper. Thank you.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Wed Jan 7 18:14:14 2026
    From Newsgroup: comp.lang.c

    On Wed, 07 Jan 2026 07:46:40 -0800
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    [regarding rand() and srand()]

    Pay attention that C Standard only requires for the same seed to
    always produces the same sequence. There is no requirement that
    different seeds have to produce different sequences.
    So, for generator in your example, implementation like below would
    be fully legal. Personally, I wouldn't even consider it as
    particularly poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    It seems better to do, for example,

    void srand(unsigned seed ) { init = seed - !seed;}


    Yes, it is better.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jan 7 08:41:25 2026
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:

    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:
    [...]
    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. O?Neill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.

    I don't know. Testing randomness is complicated matter.
    How can I be sure that L'Ecuyer and Simard's TestU01 suite tests
    things that I personally care about and that it does not test
    things that are of no interest for me? Especially, the latter.

    Do you think any of the tests in the TestU01 suite are actually counter-indicated? As long as you don't think any TestU01 test
    makes things worse, there is no reason not to use all of them.
    You are always free to disregard tests you don't care about.

    Also, the TestU01 suit is made for generators with 32-bit output.
    M. O'Neill used ad hoc technique to make it applicable to
    generators with 64-bit output. Is this technique right? Or may
    be it put 64-bit PRNG at unfair disadvantage?

    As long as the same mapping is applied to all 64-bit PRNGs under
    consideration I don't see a problem. The point of the test is to
    compare PRNGs, not to compare test methods. If someone thinks a
    different set of tests is called for they are free to run them.

    Besides, I strongly disagree with at least one assertion made by
    O'Neill: "While security-related applications should use a secure
    generator, because we cannot always know the future contexts in
    which our code will be used, it seems wise for all applications to
    avoid generators that make discovering their entire internal state
    completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a
    defect.

    You and she are talking about different things. You are talking
    about choosing a PRNG to be used only by yourself. She is talking
    about choosing a PRNG to be made available to other people without
    knowing who they are or what their needs are. In the second case
    it's reasonable to raise the bar for the set of criteria that need
    to be met.

    Anyway, even if I am skeptical about her criticism of popular PRNGs, intuitively I agree with the constructive part of the article - medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    After looking at one of the example PCG generators, I would
    describe it as a medium-quality PRNG that feeds a low-quality
    hash. The particular combination I looked at produced good
    results, but it isn't clear which combinations of PRNG and
    hash would do likewise.

    On related note, I think that even simple counter fed into high
    quality hash function (not cryptographically high quality, far
    less than that) can produce excellent PRNG with even smaller
    internal state. But not very fast one. Although the speed
    depends on specifics of used computer. I can imagine computer
    that has low-latency Rijndael128 instruction. On such computer,
    running counter through 3-4 rounds of Rijndael ill produce very
    good PRNG that is only 2-3 times slower than, for example, LCG
    128/64.

    I think the point of her paper where she talks about determining
    how much internal state is needed is to measure the efficacy of
    the PRNG, not to try to reduce the amount of state needed. Based
    on my own experience with various PRNGs I think it's a mistake to
    try to minimize the amount of internal state needed. My own rule
    of thumb is to allow at least a factor of four: for example, a
    PRNG with a 32-bit output should have at least 128 bits of state.
    My latest favorite has 256 bits of state to produce 32-bit
    outputs (and so might also do well to produce 64-bit outputs, but
    I haven't tested that).
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Jan 7 13:54:21 2026
    From Newsgroup: comp.lang.c

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    John McCue <jmclnx@gmail.com.invalid> writes:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    It does provide an srand_deterministic() function that behaves the way
    srand() is supposed to.

    And a program that calls rand() produces a link-time warning, even
    though OpenBSD's rand() *doesn't* return deterministic values.

    ld: warning: rand_test.c(rand_test.o:(main)): warning: rand() may return deterministic values, is that what you want?

    (In a similarly questionable decision, OpenBSD's printf triggers a
    SIGABRT signal if the format string uses "%n".)
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu Jan 8 01:06:01 2026
    From Newsgroup: comp.lang.c

    On Wed, 07 Jan 2026 08:41:25 -0800
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:
    [...]
    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. O?Neill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.

    I don't know. Testing randomness is complicated matter.
    How can I be sure that L'Ecuyer and Simard's TestU01 suite tests
    things that I personally care about and that it does not test
    things that are of no interest for me? Especially, the latter.

    Do you think any of the tests in the TestU01 suite are actually counter-indicated? As long as you don't think any TestU01 test
    makes things worse, there is no reason not to use all of them.
    You are always free to disregard tests you don't care about.


    Except that it's difficult psychologically.
    The batteries of test gains position of of authority in your mind.
    Well, may be, you specifically are resistant, but I am not. Nor is
    Melissa O'Nail, it seems.

    To illustrate my point, I will tell you the story about myself.
    Sort of confession.
    If you had read the rest of this thread (or paid attention to
    finer details in the article of O'Nail) then you already know that
    mt19937 consistently fails in scomp_LinearComp() subtest of Crush and
    BigCrush batteries of Test01. As reported, I "fixed" it by skipping
    every 19936th word of mt19937 output. This particular "fix" is benign.
    I am sure that it does not make output of generator less random. The
    only impact is a bit of slowness, because of the need to manage yet
    another modulo counter.
    What I did not tell so far is that I tried another "fix". I added
    leakage during mt state update. It means that periodically I
    forced two LS bits of newly generated state word to be '01', without
    affecting the word that goes to tampering and then to output.
    And according to Test01 it worked! Flew through all batteries!
    Luckily, I was sufficiently self-conscious to understand that I don't understand nearly enough about algebra of Galois fields to predict all consequences of my modification. But that's me. I know few people that
    are less aware of their limitations.

    Also, the TestU01 suit is made for generators with 32-bit output.
    M. O'Neill used ad hoc technique to make it applicable to
    generators with 64-bit output. Is this technique right? Or may
    be it put 64-bit PRNG at unfair disadvantage?

    As long as the same mapping is applied to all 64-bit PRNGs under consideration I don't see a problem. The point of the test is to
    compare PRNGs, not to compare test methods. If someone thinks a
    different set of tests is called for they are free to run them.


    But Melissa, following advice of L'Ecuyer, and me, following advice of
    Melissa, tested 64-bit generators three times - LSW then MSW, only LSW
    and only MSW, thus putting them under trice more serious scrutiny than
    32-bit counterparts.

    Besides, I strongly disagree with at least one assertion made by
    O'Neill: "While security-related applications should use a secure generator, because we cannot always know the future contexts in
    which our code will be used, it seems wise for all applications to
    avoid generators that make discovering their entire internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a
    defect.

    You and she are talking about different things. You are talking
    about choosing a PRNG to be used only by yourself. She is talking
    about choosing a PRNG to be made available to other people without
    knowing who they are or what their needs are. In the second case
    it's reasonable to raise the bar for the set of criteria that need
    to be met.


    No, this part of her article is a mistake, plain and simple.
    He even sort of admitted it couple of years later in her blogg.
    In reality, we either need secure PRNG or do not need secure PRNG.
    There is no middle ground of "more or less secure insecure PRNGs".
    PRNGs she advocates are insecure until proven otherwise by crypto
    analysts.

    Anyway, even if I am skeptical about her criticism of popular PRNGs, intuitively I agree with the constructive part of the article - medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    After looking at one of the example PCG generators, I would
    describe it as a medium-quality PRNG that feeds a low-quality
    hash. The particular combination I looked at produced good
    results, but it isn't clear which combinations of PRNG and
    hash would do likewise.


    I tend to like CRC32C for hashing 64 bits into 32 bits; for no reason
    apart from that this primitive is available and fast on modern Intel
    and AMD CPUs. ARM64 CPUs as well, I think, although less than 100% sure.

    On related note, I think that even simple counter fed into high
    quality hash function (not cryptographically high quality, far
    less than that) can produce excellent PRNG with even smaller
    internal state. But not very fast one. Although the speed
    depends on specifics of used computer. I can imagine computer
    that has low-latency Rijndael128 instruction. On such computer,
    running counter through 3-4 rounds of Rijndael ill produce very
    good PRNG that is only 2-3 times slower than, for example, LCG
    128/64.

    I think the point of her paper where she talks about determining
    how much internal state is needed is to measure the efficacy of
    the PRNG, not to try to reduce the amount of state needed. Based
    on my own experience with various PRNGs I think it's a mistake to
    try to minimize the amount of internal state needed. My own rule
    of thumb is to allow at least a factor of four: for example, a
    PRNG with a 32-bit output should have at least 128 bits of state.
    My latest favorite has 256 bits of state to produce 32-bit
    outputs (and so might also do well to produce 64-bit outputs, but
    I haven't tested that).

    One important point that I seem to figure out recently is that the only practical way to produce both solid and very fast PRNG that adheres to
    standard language APIs with 32-bit and to somewhat smaller extent 64-bit output, is to use buffering. I.e. most of the time generator simply
    reads pre-calculated word from the buffer and only ones per N
    iterations runs an actual PRNG algorithm, probably in a loop, often
    in SIMD. In order for this approach to be effective, buffer can't be particularly small. 32 bytes (256 bits) appear to be an absolute
    minimum. The buffer and counter that manages buffering, are parts of the generator state. That alone sets a practical minimal limit on the size
    of generator and diminishes significance of the difference between
    PRNGs with "algorithmic" state of 64 bits, 128 bits or even 256 bits.

    The observation certainly applies to PCGs or to anything else that
    utilizes LCG for its state update primitive.

    Now, if one does no look for ultimate speed then said above does not
    apply.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Thu Jan 8 02:57:08 2026
    From Newsgroup: comp.lang.c

    On Mon, 22 Dec 2025 08:48:10 -0000 (UTC), Michael Sanders wrote:

    [...]

    This puppy is done (at least I'm happy with it).

    Main thing I wanted to accomplish was solid keyboard
    handling in the REPL (so I can apply the knowledge
    gained to another project & I believe I'm pretty close.)

    As it stands the game now has:

    - 3 games in a single binary (mastermind, moo, bagels)

    - man page

    - make file

    - sparse comments...

    Zipped sources [8.3KB] for a few days below:

    <https://drive.google.com/file/d/1PAzCj-Mfx06y2oHz9LQTxa74Aerd2pYe/view?usp=sharing>

    Thanks for the help everyone, I'm gone fishin' for awhile =)
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Thu Jan 8 14:03:35 2026
    From Newsgroup: comp.lang.c

    On Wed, 7 Jan 2026 10:51:16 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:
    Michael S <already5chosen@yahoo.com> wrote:
    I experimented a bit more (in fact, more like a lot more) with
    test batteries of LrCOEcuyer. It led me to conclusion that occasional failure in the either middle or big battery means nothing.
    Sometimes even cripto-quality PRNG does not pass one or another
    test. Then you try to reproduce it and see that with any other seed
    that you try a failure does not happen.
    All in all, it makes me more suspect of PRNGs that consistently pass
    both batteries with various seed. I start to see it as a sign of
    PRNG being rigged to pass tests.

    Well, that depends on the tests and threshhold in the tests.
    Some tests when fed with trurly random source will produce produce
    very small variation of the results. With generous threshhold
    such test will essentially never fail for trurly random source.
    OTOH when expected variation of the results is larger and
    threshhold is tight, then trurly random source will fail the
    test from time to time.
    Yes, there are many stable tests. But there are also many unstable
    tests in Crash/BigCrash batteries. I can't say if the ratio is 50:50 or
    75:25, but it is not 90:10.
    svaria_WeightDistrib() and swalk_RandomWalk1() appear most unstable.
    You feed them with particular pseudo-random vector (100,000,000 points)
    and the test fails. Then you shift the vector by just one point, to the
    left or to the right. And it passes, often not just passes, but produces
    a result that is not even close to the margin.
    And if you test long enough you should
    be able to estimate probability of failure and possibly compare
    is with theoretical result if available.

    Long enough in this case is very damn long.
    To say the truth, I do not know what failures of crypto-quality PRNG
    means. It may mean that the test is of tight kind that is supposed
    to fail from time to time for trurly random source. Or it may mean
    to PRNG improves cypto part at cost of statistics. That is
    non-uniform distribution of the output is not a problem in
    crypto applications. Simply for crypto purposes future output
    should be not predictable from current and past output only.
    And slightly non-uniform distribution can increase probablity
    of test failure enough that you can observe such failures.

    BTW: You mentioned using counter and hardware AES128 round.
    Given cheap AES128 round I would try 128-bit state and AES128
    round as state update. I do not know if hardware AES128 is
    fast enough to make it wortwhile, but using AES128 round as a
    state update should be much better than scrambling a counter.

    It depends on what one considers fast.
    My expectations, trained by LCGs and esp. by optimized MT, are rather
    high. [On relatively modern Intel and AMD hardware] generators that do
    feedback through even a single AES round (with another round for post-processing) do not meet my expectations.
    At best, fully inlined and after significant effort of removing all non-necessary bottlenecks, such generators produce one output word per
    4-5 clock cycles. More naive implementations with state read from
    memory and stored back to memory on each iteration, are much slower
    than that, easily takes over 10 clocks per iteration.
    For comparison, a counter that goes through 5 rounds of AES without
    feedback runs at 3 clocks per word when inlined and at 3.5 clocks per
    word via function call.
    It seems that given today's hardware limitations the only way to get
    really fast PRNG with the state updated through AES round is by running
    several chains interleaved. Preferably no less than 4 chains. Which
    increases size of basic PRNG state to 512 bits + some more for state management.
    Another problem is that when all state bits go through AES then I can
    not prove that the period of PRNG is really high. I can't even be sure
    that it does not enter very short loop at some stage of its life. I
    understand that it is highly unlikely, but don't like uncertainty.
    Running half of the state through AES and taking another half from
    counter solves it, but at cost of need for better diffusion in
    post-processing (temper) state of generator. If more than 32 bits of
    output produced per iteration, I would not feel good if tempering
    consists of just one round of AES. I would want two rounds at least.
    May be, better option is to do something like that:
    interleave N times {
    prnd_out = AES1R(state.w128);
    state.w128 = AES1R(state.w128) ^ state.counter64;
    state.counter64 = state.counter64 + 1;
    }
    I am still not 100% sure that the long period is guaranteed with such arrangement, but I am far more sure of it than in case of original
    arrangement.
    However, it is far from simple and the state is not very small. So, the question is "Why bother"? What's wrong with much simpler scheme that
    takes 64-bit counter and runs it through 5 AES rounds? Or even 6 round
    if being paranoid?
    The only downside that I can see is that this simple robust scheme is
    more dependent on high throughput of AES units which is not necessarily available on somewhat older hardware.
    Said above does not apply to scomp_LinearComp() failures of mt19937.
    Those failures are very consistent. I just don't consider them
    significant for my own use of PRNGs or for any other uses of PRNG
    that I personally ever encountered.

    Overall an experience strengthened my position that general wisdom, previously shared by O'Neil herself, got it right: in absence of the special considerations people should select mt19937 and especially mt19937-64 as their default PRNGs of choice.
    Looking closer, apart from its properties of randomness and apart
    from huge period

    Huge period alone is easy. AFAICS matrix variants of LCG can
    easily get quite large periods. I did not test matrix LCG,
    but on statistical tests they should be essentially as good
    as multiprecision LCG, but should be cheaper to implement.

    Just to be clear, I mean equation x_{n+1} = Ax_n + b, wher x_n
    is a vector reprezenting n-th state, A is a matrix and b is a
    vector. Matrix A may be somewhat sparse, that is have limited
    number of non-zero entries, and some entries my be simple, like
    1 or -1. With proper choice of a and b period should be
    comparable with number of availalable states.

    I see no reason to go for very long periods, already 512 bits
    of state allow perids which in practice should be indistingushable
    from infinite period.

    Didn't I agree with that in the very next statement?
    (which does not matter for me) I started to appreciate for
    mt19937 for the following properties that I was not aware before:
    - it does not use multiplier. So good fit for embedded systems that
    have no (or very slow) multiplier HW.

    Biggest MCU with no hardware multiplier that I have has 2kB RAM.
    I do not want mt19937 on such a machine. 64-bit multiplication
    on 8-biter with hardware multiplier may be slow, so 64-bit LCG
    (and improvements based on it) may be slow. But multiplication
    nicely spreads out bits, so it is not clear to me if there is
    equally good cheaper alternative.
    64-bit multipliers spread bits nicely, indeed. But implementing 64-bit multiplier on the core that natively has only 8x8=16 is not easy or
    fast.
    Even implementing 32-bit multiplier here is not easy or fast. And I
    would not say that 32-bit multipliers spread bits nicely.
    But I was not thinking about that sort of cores.
    I had in mind minimalistic implementation of soft core architectures by
    Xilinx and Altera.
    These core have several useful properties:
    - tested and supported much better than open-source alternatives
    - occupy relative few logic and block RAM resources
    - often capable to run at higher frequency than their full-featured
    brethren
    - the fact that they can be used without buying a license (free as
    beer) does not hurt either
    Of course, they are *much* slower (lower IPC) than full-featured cores,
    but in plenty of cases it's o.k. One thing that these cores do not have
    is multiplier. When compiler sees multiplication in source code it calls support routine. And when it happens then instead of *much* slower
    (say, 5x) than full-featured core, which is commonly acceptable, these
    cores turn ALOT slower (say, 200x0 which is commonly unacceptable.
    Cores like that sometimes used in applications that want good PRNG,
    like various sorts of built-in tests. In such app spending 2K bytes for
    mt1937 state is o.k. Using emulated 64-bit multiplication for PRMG is
    sometimes o.k. but more commonly not so.
    If needed I would investigate
    matrix LCG, they may be slightly cheaper.

    - algorithm is very SIMD-friendly, so optimized implementations can
    be very very fast on modern x86 and ARM64 hardware.

    Just size of the state puts limit how fast it can be. And size of
    the state means that it will compete for cache with user data.
    BTW: AFAICS matrix LCG can be SIMD friendly too.

    The latter property also means that very fast FPGA implementation is
    easily possible as long as designer is willing to through at it
    moderate amount of logic resources.

    Said above does not mean that PCG generators of O'Neil have no
    place. Intuitively, they appear not bad. But the theory is
    unproven, optimized implementation is likely slower that optimized
    mt19937, claimed "security" advantages are nonsense as admitted
    later by O'Neil herself. And, as said above, I no longer trust her empirical methodology, based on work of LrCOEcuyer.
    So, PCG generators are valuable addition to the toolbox, but not
    good enough to change my default.

    I agree that ATM it is not entirely clear if PCG-s are as good as
    suggested by tests. But I am surprised by your opinion about
    speed, I did not analyze deeply either of them, but PCG-s are way
    simpler, so I would expect them to be faster.

    When used in simple way, PCG has LCG latency (IMUL+ADD) embedded in its
    core.
    On modern x86-64 it tends to be 4 clocks at very least. On ARM64 it's
    often better, by I am more interested in x86-64.
    OTOH, optimized implementations of mt19937 have [almost] no inherent
    latency limits. If one has wider hardware it directly translate into
    both faster state update and faster tampering phase.
    More strictly speaking for state update there exists a strict
    algorithmic limit of 397 32-bit words. Plus, non-heroic implementations
    have limit of 624-397=237 words. Both limits are far away from
    capabilities of the widest commonly available hardware (2x or 3x 512
    bits, i.e. no more than 48 32-bit operations per cycle).
    In practice, the biggest bottleneck , even without 512-bit processing,
    is not a state update and not tampering, but fighting impedance
    mismatch at the level of API. That is, implementation prefers to
    generate 256 bits at once, but API insists on 32 bits. Which leads to
    need for buffering and for additional management overhead. More time
    spent here than within mt algorithm itself.
    Still, even with overhead, when buffer management part is inlined
    (which is easy to achieve in practice and does not cause significant
    code bloat) modern but not the most modern Intel core (Raptor Cove)
    that I have in my desktop at work is capable to deliver one 32-bit word
    per exactly 3 clock cycles.
    That's a little better than even most basic fully inlined LCG, so
    necessarily better than any PCG.
    There is little doubt that LCGs (and as result of it PCGs) can be
    improved via advancing state by several steps at time instead of one
    step at time. But then they will suffer from the same impedance
    mismatch with APIs. Solution to mismatch would be the same as with mt - buffering and additional management overhead. So gone simplicity, gone
    tiny state size :(
    I did not try it, but it is very likely that for given width of output
    word the resulting speed of PCG would be almost exactly the same as
    optimized mt19937, because generator will spend majority of time in the
    code that not merely almost the same between the two, but exactly the
    same.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Thu Jan 8 15:34:04 2026
    From Newsgroup: comp.lang.c

    On Wed, 07 Jan 2026 13:54:21 -0800, Keith Thompson wrote:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    John McCue <jmclnx@gmail.com.invalid> writes:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    It does provide an srand_deterministic() function that behaves the way srand() is supposed to.

    So then clang would use:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But I don't know (yet) that gcc does as well under OpenBSD.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Thu Jan 8 09:26:18 2026
    From Newsgroup: comp.lang.c

    antispam@fricas.org (Waldek Hebisch) writes:

    Michael S <already5chosen@yahoo.com> wrote:
    [...]
    Anyway, even if I am skeptical about her criticism of popular PRNGs,
    intuitively I agree with the constructive part of the article -
    medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    She seem to care very much about having minimal possible state.
    That is may be nice on embeded systems, but in general I would
    happily accept slighty bigger state (say 256 bits). But if
    we can get good properties with very small state, then why not?
    [...]

    That depends on whether one thinks the tests done to measure
    quality are sufficient to determine all the axes of "good
    properties". I'm not inclined to think that they do. The
    set of tests done in the TestU01 suite are quite good IMO,
    but I don't think they measure all the properties of interest.
    I prefer to err on the side of caution.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Thu Jan 8 09:40:21 2026
    From Newsgroup: comp.lang.c

    antispam@fricas.org (Waldek Hebisch) writes:

    Michael S <already5chosen@yahoo.com> wrote:

    I experimented a bit more (in fact, more like a lot more) with
    test batteries of L?Ecuyer. It led me to conclusion that occasional
    failure in the either middle or big battery means nothing.
    Sometimes even cripto-quality PRNG does not pass one or another test.
    Then you try to reproduce it and see that with any other seed that you
    try a failure does not happen.
    All in all, it makes me more suspect of PRNGs that consistently pass
    both batteries with various seed. I start to see it as a sign of
    PRNG being rigged to pass tests.

    Well, that depends on the tests and threshhold in the tests.
    Some tests when fed with trurly random source will produce produce
    very small variation of the results. With generous threshhold
    such test will essentially never fail for trurly random source.
    OTOH when expected variation of the results is larger and
    threshhold is tight, then trurly random source will fail the
    test from time to time. And if you test long enough you should
    be able to estimate probability of failure and possibly compare
    is with theoretical result if available.

    It's inherent in the nature of statistical testing that every
    so often a statistical test will "fail" even for a truly random
    input. An input source that never fails can also be indicative
    of a low quality source (and perhaps one that was tuned to the
    particular set of tests being done). It took me a while to
    learn that the results of a PRNG test suite should not be seen
    as purely binary.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu Jan 8 14:44:27 2026
    From Newsgroup: comp.lang.c

    Michael Sanders <porkchop@invalid.foo> writes:
    On Wed, 07 Jan 2026 13:54:21 -0800, Keith Thompson wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    [...]
    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    It does provide an srand_deterministic() function that behaves the way
    srand() is supposed to.

    So then clang would use:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But I don't know (yet) that gcc does as well under OpenBSD.

    I don't know what you mean when you say that clang "would use"
    that code.

    I'm not aware that either clang or gcc uses random numbers
    internally. I don't know why they would.

    You could certainly write the above code and compile it with either
    gcc or clang (or any other C compiler on OpenBSD). I've confirmed
    that gcc on OpenBSD does predefine the symbol __OpenBSD__. There
    should be no relevant difference between gcc and clang; random
    number generation is implemented in the library, not in the compiler.

    If your point is that a programmer using either gcc or clang could
    use the above code to get the required deterministic behavior
    for rand(), I agree. (Though it shouldn't be necessary; IMHO the
    OpenBSD folks made a very bad decision.)

    Relatedly, the NetBSD implementation of rand() is conforming, but
    of very low quality. The low-order bit alternates between 0 and
    1 on successive rand() calls, the two low-order bits repeat with
    a cycle of 4, and so on. Larry Jones wrote about it here in 2010:

    The even/odd problem was caused at Berkeley by a well meaning
    but clueless individual who increased the range of the generator
    (which originally matched the sample implementation) by returning
    the *entire* internal state rather than just the high-order
    bits of it. BSD was very popular, so that defective generator
    got around a lot, unfortunately.

    And I've just discovered that the OpenBSD rand() returns alternating
    odd and even results after a call to srand_determinstic().

    It's disturbing that this has never been fixed.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Fri Jan 9 06:06:57 2026
    From Newsgroup: comp.lang.c

    On Thu, 08 Jan 2026 14:44:27 -0800, Keith Thompson wrote:

    Michael Sanders <porkchop@invalid.foo> writes:
    On Wed, 07 Jan 2026 13:54:21 -0800, Keith Thompson wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    [...]
    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    It does provide an srand_deterministic() function that behaves the way
    srand() is supposed to.

    So then clang would use:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But I don't know (yet) that gcc does as well under OpenBSD.

    I don't know what you mean when you say that clang "would use"
    that code.

    I'm not aware that either clang or gcc uses random numbers
    internally. I don't know why they would.

    Well, I meant the macro itself is (I'm guessing) probably defined
    by clang since its the default compiler.

    You could certainly write the above code and compile it with either
    gcc or clang (or any other C compiler on OpenBSD). I've confirmed
    that gcc on OpenBSD does predefine the symbol __OpenBSD__. There
    should be no relevant difference between gcc and clang; random
    number generation is implemented in the library, not in the compiler.

    This is the info I'm, wondering about: both clang & gcc predefine
    the symbol.

    If your point is that a programmer using either gcc or clang could
    use the above code to get the required deterministic behavior
    for rand(), I agree. (Though it shouldn't be necessary; IMHO the
    OpenBSD folks made a very bad decision.)

    Relatedly, the NetBSD implementation of rand() is conforming, but
    of very low quality. The low-order bit alternates between 0 and
    1 on successive rand() calls, the two low-order bits repeat with
    a cycle of 4, and so on. Larry Jones wrote about it here in 2010:

    The even/odd problem was caused at Berkeley by a well meaning
    but clueless individual who increased the range of the generator
    (which originally matched the sample implementation) by returning
    the *entire* internal state rather than just the high-order
    bits of it. BSD was very popular, so that defective generator
    got around a lot, unfortunately.

    And I've just discovered that the OpenBSD rand() returns alternating
    odd and even results after a call to srand_determinstic().

    It's disturbing that this has never been fixed.

    Yikes! Thanks Keith. This is sort of odd for OpenBSD.
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu Jan 8 22:46:42 2026
    From Newsgroup: comp.lang.c

    Michael Sanders <porkchop@invalid.foo> writes:
    On Thu, 08 Jan 2026 14:44:27 -0800, Keith Thompson wrote:
    Michael Sanders <porkchop@invalid.foo> writes:
    [...]
    So then clang would use:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But I don't know (yet) that gcc does as well under OpenBSD.

    I don't know what you mean when you say that clang "would use"
    that code.

    I'm not aware that either clang or gcc uses random numbers
    internally. I don't know why they would.

    Well, I meant the macro itself is (I'm guessing) probably defined
    by clang since its the default compiler.

    You mean the macro __OpenBSD__? Yes, that and other similar macros
    are predefined by the compiler, which is configured for each OS.
    gcc on OpenBSD also predefines it. (I don't know whether it's
    predefined by the preprocessor directly or by some header that's
    included implicitly. That doesn't really matter.) Compilers on
    other platforms will not predefine __OpenBSD__.

    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    [...]
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Fri Jan 9 00:36:21 2026
    From Newsgroup: comp.lang.c

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    John McCue <jmclnx@gmail.com.invalid> writes:

    Michael Sanders <porkchop@invalid.foo> wrote:

    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    Apparently the OpenBSD folks have seen fit to remove the only
    desirable property that ISO C actually specifies for the standard
    library random number generator. Bravo!
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael Sanders@porkchop@invalid.foo to comp.lang.c on Fri Jan 9 22:38:59 2026
    From Newsgroup: comp.lang.c

    On Thu, 08 Jan 2026 22:46:42 -0800, Keith Thompson wrote:

    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    Well, under OpenBSD I plan on using:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But what I was asking is whether or not gcc would recognize
    the __OpenBSD__ macro (why wouldn't I'm assuming) since clang
    is the default compiler.

    But also about srand()... you've got me really wondering why
    OpenBSD would deviate from the standard as they have. I get
    that the those folks disagree because its deterministic, but
    its the accepted standard to be deterministic with srand().

    Only speaking for myself here, rather than srand_deterministic()
    and srand() (that's not deterministic under OpenBSD) it
    would've made more sense to've implemented srand_non_deterministic()
    and left srand() alone. That design decision on their part only
    muddies the waters in my thinking. Live & learn =)
    --
    :wq
    Mike Sanders
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Fri Jan 9 23:27:15 2026
    From Newsgroup: comp.lang.c

    Michael Sanders <porkchop@invalid.foo> writes:
    On Thu, 08 Jan 2026 22:46:42 -0800, Keith Thompson wrote:

    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    Well, under OpenBSD I plan on using:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But what I was asking is whether or not gcc would recognize
    the __OpenBSD__ macro (why wouldn't I'm assuming) since clang
    is the default compiler.

    $ gcc -dM -E - < /dev/null

    will show all the preprocessor macros predefined by the compiler.

    There 397 predefined macros on my Fedora gcc 14 installation.

    Note that other macros may be defined in header files.



    But also about srand()... you've got me really wondering why
    OpenBSD would deviate from the standard as they have. I get
    that the those folks disagree because its deterministic, but
    its the accepted standard to be deterministic with srand().

    I expect they were primary concerned with the security
    implications of a deterministic algorithm.


    Only speaking for myself here, rather than srand_deterministic()
    and srand() (that's not deterministic under OpenBSD) it
    would've made more sense to've implemented srand_non_deterministic()
    and left srand() alone. That design decision on their part only
    muddies the waters in my thinking. Live & learn =)

    I'm sure they wanted the change to apply by default to existing
    applications (many of them likely distributed with various BSD
    releases).

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Fri Jan 9 17:09:47 2026
    From Newsgroup: comp.lang.c

    Michael Sanders <porkchop@invalid.foo> writes:
    On Thu, 08 Jan 2026 22:46:42 -0800, Keith Thompson wrote:
    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    Well, under OpenBSD I plan on using:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But what I was asking is whether or not gcc would recognize
    the __OpenBSD__ macro (why wouldn't I'm assuming) since clang
    is the default compiler.

    OK.

    Do you understand that your original question was unclear?

    You said that "clang would use" the quoted 5-line code snippet,
    and asked whether "gcc does as well". It's not clang or gcc that
    would use that code. It would be used by a programmer writing code
    to be compiled with clang or gcc.

    I understand now what you meant. I'd like to be sure that you
    understand the problem with the question as you originally wrote it.

    I have clang 19.1.7 and gcc 13.2.0 installed on OpenBSD 7.8, and
    both predefine the macro __OpenBSD__.

    But also about srand()... you've got me really wondering why
    OpenBSD would deviate from the standard as they have. I get
    that the those folks disagree because its deterministic, but
    its the accepted standard to be deterministic with srand().

    Only speaking for myself here, rather than srand_deterministic()
    and srand() (that's not deterministic under OpenBSD) it
    would've made more sense to've implemented srand_non_deterministic()
    and left srand() alone. That design decision on their part only
    muddies the waters in my thinking. Live & learn =)

    I don't know why they made that decision. It was clearly deliberate.
    I agree that adding an srand_non_deterministic() function would
    have been better.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@046-301-5902@kylheku.com to comp.lang.c on Sat Jan 10 19:44:01 2026
    From Newsgroup: comp.lang.c

    On 2026-01-09, Michael Sanders <porkchop@invalid.foo> wrote:
    On Thu, 08 Jan 2026 22:46:42 -0800, Keith Thompson wrote:

    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    Well, under OpenBSD I plan on using:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    This is is better

    // In some common configuration header:

    #ifdef __OpenBSD__
    #define HAVE_SRAND_DETERMINISTIC 1
    #define HAVE_... /* other such macros */
    #endif

    (Or the configuration can be generated by scripts which detect features
    in environment.)

    Then in the code:

    #if HAVE_SRAND_DETERMINISTIC
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    If a platform other than __OpenBSD__ comes along which has
    srand_deterministic you just make sure HAVE_SRAND_DETERMINISTIC 1 is
    turned on; you don't have to edit the code where that is used.

    This idea is seen in the configuration of GNU programs and such.

    There is a "GDB Internals" document which discusses it in a section
    called "Clean Design"

    Partial quote:

    New #ifdefrCOs which test for specific compilers or manufacturers or
    operating systems are unacceptable. All #ifdefrCOs should test for
    features. The information about which configurations contain which
    features should be segregated into the configuration files. Experience
    has proven far too often that a feature unique to one particular system
    often creeps into other systems; and that a conditional based on some
    predefined macro for your current system will become worthless over
    time, as new versions of your system come out that behave differently
    with regard to this feature.

    [ ... more discussion ... ]

    https://www.sourceware.org/gdb/5/onlinedocs/gdbint.pdf

    I think the GNU Coding Standards document may have had a similar
    discussion; I don't see it in the current version though.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2