• Re: how cast works?

    From Dan Purgert@21:1/5 to Thiago Adams on Wed Aug 7 20:00:28 2024
    On 2024-08-07, Thiago Adams wrote:
    How cast works?
    Does it changes the memory?
    For instance, from "unsigned int" to "signed char".
    Is it just like discarding bytes or something else?
    [...]

    I don't know what happens when you're changing datatype lengths, but if
    they're the same length, it's just telling the compiler what the
    variable should be treated as (e.g. [8-bit] int to char)


    I also would like to understand better signed and unsigned.
    There is no such think as "signed" or "unsigned" register, right?

    "Signed" just means the first bit indicates negative.

    So an "unsigned" 8 bit integer will have the 256 values ranging from

    0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 = 0
    (0b00000000)

    TO

    128 + 64 + 32 + 16 + 8 +4 + 2 + 1 = 255
    (0b11111111)


    Whereas a "signed" 8 bit integer will have the 256 values ranging from

    (-128) + 0 + 0 + 0 + 0 + 0 + 0 + 0 = -128
    (0b10000000)

    TO

    0 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127
    (0b01111111)

    At least in two's compliment (but that's the way it's done in C)


    How about floating point?

    Floating point is a huge mess, and has a few variations for encoding;
    though I think most C implementations use the one from the IEEE on 1985
    (uh, IEEE754, I think?)



    --
    |_|O|_|
    |_|_|O| Github: https://github.com/dpurgert
    |O|O|O| PGP: DDAB 23FB 19FA 7D85 1CC1 E067 6D65 70E5 4CE7 2860

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Thiago Adams on Wed Aug 7 23:03:31 2024
    On Wed, 7 Aug 2024 08:28:09 -0300, Thiago Adams wrote:

    The problem is that this causes an explosion of combinations that I am
    trying to avoid.

    The only way I can think of to minimize the combinatorial explosion is to
    look at the precise types you are dealing with, and do some grouping of
    them, doing the conversion in two steps via some intermediate “universal” type, which is different for each group. E.g.

    * Is either the source or destination type a floating-point type? Then use
    the highest-available-precision float type as the intermediate type.
    * Are both source and destination types integer types? Then use some largest-available integer type as the intermediate type.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Dan Purgert on Wed Aug 7 23:00:17 2024
    On Wed, 7 Aug 2024 20:00:28 -0000 (UTC), Dan Purgert wrote:

    Floating point is a huge mess ...

    That mess has gone away with the essentially universal adoption of
    IEEE754. There were a few hardware stragglers back in the 1990s, but even
    they have come around by now.

    Not sure if Java has caught up yet, though ...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Dan Purgert on Thu Aug 8 12:44:46 2024
    Dan Purgert <[email protected]> wrote or quoted:
    I don't know what happens when you're changing datatype lengths, but if >they're the same length, it's just telling the compiler what the
    variable should be treated as (e.g. [8-bit] int to char)

    Casting doesn't tweak a value right where it sits, so you don't
    have to stress about resizing memory. (It hands you an rvalue,
    not an lvalue.)

    Casting isn't just about variables; it's all about expressions.

    The whole casting concept hails from Algol.

    Basically, casting is like flipping a value from one type
    to another (as specified by the cast).

    But you got to tackle each pair of data types on its own,
    and that's way more than we can dive into here!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Purgert@21:1/5 to Stefan Ram on Thu Aug 8 14:08:14 2024
    On 2024-08-08, Stefan Ram wrote:
    Dan Purgert <[email protected]> wrote or quoted:
    I don't know what happens when you're changing datatype lengths, but if >>they're the same length, it's just telling the compiler what the
    variable should be treated as (e.g. [8-bit] int to char)

    Casting doesn't tweak a value right where it sits, so you don't
    have to stress about resizing memory. (It hands you an rvalue,
    not an lvalue.)

    Yeah, I have to admit I've been driving myself mad trying to learn
    AVR-Assembly lately, so ... types don't exist :|

    --
    |_|O|_|
    |_|_|O| Github: https://github.com/dpurgert
    |O|O|O| PGP: DDAB 23FB 19FA 7D85 1CC1 E067 6D65 70E5 4CE7 2860

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Thiago Adams on Thu Aug 8 14:23:44 2024
    On 08/08/2024 12:14, Thiago Adams wrote:
    On 07/08/2024 17:00, Dan Purgert wrote:
    On 2024-08-07, Thiago Adams wrote:
    How cast works?
    Does it changes the memory?
    For instance, from "unsigned int" to "signed char".
    Is it just like discarding bytes or something else?
    [...]

    I don't know what happens when you're changing datatype lengths, but if
    they're the same length, it's just telling the compiler what the
    variable should be treated as (e.g. [8-bit] int to char)


    I also would like to understand better signed and unsigned.
    There is no such think as "signed" or "unsigned" register, right?

    "Signed" just means the first bit indicates negative.

    So an "unsigned" 8 bit integer will have the 256 values ranging from

      0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 = 0
           (0b00000000)

                   TO

      128 + 64 + 32 + 16 + 8 +4 + 2 + 1 = 255
           (0b11111111)


    Whereas a "signed" 8 bit integer will have the 256 values ranging from

       (-128) + 0 + 0 + 0 + 0 + 0 + 0 + 0 = -128
           (0b10000000)

                   TO

       0 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127
           (0b01111111)

    At least in two's compliment (but that's the way it's done in C)


    How about floating point?

    Floating point is a huge mess, and has a few variations for encoding;
    though I think most C implementations use the one from the IEEE on 1985
    (uh, IEEE754, I think?)




    I didn't specify properly , but my question was more about floating
    point registers. I think in this case they have specialized registers.


    Try godbolt.org. Type in a fragment of code that does different kinds of
    casts (it needs to be well-formed, so inside a function), and see what
    code is produced with different C compilers.

    Use -O0 so that the code isn't optimised out of existence, and so that
    you can more easily match it to the C source.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Bart on Thu Aug 8 19:32:03 2024
    On Thu, 8 Aug 2024 14:23:44 +0100
    Bart <[email protected]> wrote:

    On 08/08/2024 12:14, Thiago Adams wrote:
    On 07/08/2024 17:00, Dan Purgert wrote:
    On 2024-08-07, Thiago Adams wrote:
    How cast works?
    Does it changes the memory?
    For instance, from "unsigned int" to "signed char".
    Is it just like discarding bytes or something else?
    [...]

    I don't know what happens when you're changing datatype lengths,
    but if they're the same length, it's just telling the compiler
    what the variable should be treated as (e.g. [8-bit] int to char)


    I also would like to understand better signed and unsigned.
    There is no such think as "signed" or "unsigned" register, right?


    "Signed" just means the first bit indicates negative.

    So an "unsigned" 8 bit integer will have the 256 values ranging
    from

      0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 = 0
           (0b00000000)

                   TO

      128 + 64 + 32 + 16 + 8 +4 + 2 + 1 = 255
           (0b11111111)


    Whereas a "signed" 8 bit integer will have the 256 values ranging
    from

       (-128) + 0 + 0 + 0 + 0 + 0 + 0 + 0 = -128
           (0b10000000)

                   TO

       0 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 127
           (0b01111111)

    At least in two's compliment (but that's the way it's done in C)


    How about floating point?

    Floating point is a huge mess, and has a few variations for
    encoding; though I think most C implementations use the one from
    the IEEE on 1985 (uh, IEEE754, I think?)




    I didn't specify properly , but my question was more about floating
    point registers. I think in this case they have specialized
    registers.


    Try godbolt.org. Type in a fragment of code that does different kinds
    of casts (it needs to be well-formed, so inside a function), and see
    what code is produced with different C compilers.

    Use -O0 so that the code isn't optimised out of existence, and so
    that you can more easily match it to the C source.




    I'd recommend an opposite - use -O2 so the cast that does nothing
    optimized away.

    int foo_i2i(int x) { return (int)x; }
    int foo_u2i(unsigned x) { return (int)x; }
    int foo_b2i(_Bool x) { return (int)x; }
    int foo_d2i(double x) { return (int)x; }

    etc
    https://godbolt.org/z/GWjbcG4GT

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Michael S on Thu Aug 8 18:29:40 2024
    On 08/08/2024 17:32, Michael S wrote:
    On Thu, 8 Aug 2024 14:23:44 +0100
    Bart <[email protected]> wrote:
    Try godbolt.org. Type in a fragment of code that does different kinds
    of casts (it needs to be well-formed, so inside a function), and see
    what code is produced with different C compilers.

    Use -O0 so that the code isn't optimised out of existence, and so
    that you can more easily match it to the C ource.




    I'd recommend an opposite - use -O2 so the cast that does nothing
    optimized away.

    int foo_i2i(int x) { return (int)x; }
    int foo_u2i(unsigned x) { return (int)x; }
    int foo_b2i(_Bool x) { return (int)x; }
    int foo_d2i(double x) { return (int)x; }
    The OP is curious as to what's involved when a conversion is done.
    Hiding or eliminating code isn't helpful in that case; the results can
    also be misleading:

    Take this example:

    void fred(void) {
    _Bool b;
    int i;
    i=b;
    }

    Unoptimised, it generates this code:

    push rbp
    mov rbp, rsp

    mov al, byte ptr [rbp - 1]
    and al, 1
    movzx eax, al
    mov dword ptr [rbp - 8], eax

    pop rbp
    ret

    You can see from this that a Bool occupies one byte; it is masked to 0/1
    (so it doesn't trust it to contain only 0/1), then it is widened to an
    int size.

    With optimisation turned on, even at -O1, it produces this:

    ret

    That strikes me as rather less enlightening!

    Meanwhile your foo_b2i function contains this optimised code:

    mov eax, edi
    ret

    The masking and widening is not present. Presumably, it is taking
    advantage of the fact that a _Bool argument will be converted and
    widened to `int` at the callsite even though the parameter type is also
    _Bool. So the conversion has already been done.

    You will see this if writing also a call to foo_b2i() and looking at the /non-elided/ code.

    The unoptimised code for foo_b2i is pretty awful (like masking twice,
    with a pointless write to memory between them). But sometimes with gcc
    there is no sensible middle ground between terrible code, and having
    most of it eliminated.

    The unoptimised code from my C compiler for foo_b2i, excluding
    entry/exit code, is:

    movsx eax, byte [rbp + foo_b2i.x]

    My compiler assumes that a _Bool type already contains 0 or 1.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Thu Aug 8 19:58:34 2024
    On 08/08/2024 19:29, Bart wrote:
    On 08/08/2024 17:32, Michael S wrote:
    On Thu, 8 Aug 2024 14:23:44 +0100
    Bart <[email protected]> wrote:
    Try godbolt.org. Type in a fragment of code that does different kinds
    of casts (it needs to be well-formed, so inside a function), and see
    what code is produced with different C compilers.

    Use -O0 so that the code isn't optimised out of existence, and so
    that you can more easily match it to the C ource.




    I'd recommend an opposite - use -O2 so the cast that does nothing optimized away.

    int foo_i2i(int x) { return (int)x; }
    int foo_u2i(unsigned x) { return (int)x; }
    int foo_b2i(_Bool x) { return (int)x; }
    int foo_d2i(double x) { return (int)x; }
    The OP is curious as to what's involved when a conversion is done.
    Hiding or eliminating code isn't helpful in that case; the results can
    also be misleading:

    Michael is correct - the OP should enable optimisation, precisely to
    avoid the issue you are concerned about. Without optimisation, the
    results are misleading - they will only show things that are /not/
    involved in the conversion, swamping the useful results with code that
    messes about putting data on and off the stack. When optimised
    compilation shows that no code is generated, it is a very clear
    indication that no operations are needed for the conversions in question
    - unoptimized code hides that.


    Take this example:

      void fred(void) {
       _Bool b;
         int i;
         i=b;
      }

    Unoptimised, it generates this code:

            push    rbp
            mov     rbp, rsp

            mov     al, byte ptr [rbp - 1]
            and     al, 1
            movzx   eax, al
            mov     dword ptr [rbp - 8], eax

            pop     rbp
            ret

    You can see from this that a Bool occupies one byte; it is masked to 0/1
    (so it doesn't trust it to contain only 0/1), then it is widened to an
    int size.


    No, you can't see that. All you can see is garbage in, garbage out.
    You have to start with a function that has some meaning!

    With optimisation turned on, even at -O1, it produces this:

            ret

    Try again with:

    int foo(bool x) { return x; }

    bool bar(int x) { return x; }

    Try it with -O0 and -O1, and then tell us which you think gives a
    clearer indication of the operations needed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Thiago Adams on Thu Aug 8 19:01:56 2024
    On 08/08/2024 18:50, Thiago Adams wrote:
    On 08/08/2024 14:29, Bart wrote:
    On 08/08/2024 17:32, Michael S wrote:
    On Thu, 8 Aug 2024 14:23:44 +0100
    Bart <[email protected]> wrote:
    Try godbolt.org. Type in a fragment of code that does different kinds >>  >> of casts (it needs to be well-formed, so inside a function), and see
    what code is produced with different C compilers.
    ;
    Use -O0 so that the code isn't optimised out of existence, and so
    that you can more easily match it to the C ource.
    ;
    ;
    ;
    ;
    I'd recommend an opposite - use -O2 so the cast that does nothing
    optimized away.
    ;
    int foo_i2i(int x) { return (int)x; }
    int foo_u2i(unsigned x) { return (int)x; }
    int foo_b2i(_Bool x) { return (int)x; }
    int foo_d2i(double x) { return (int)x; }
    The OP is curious as to what's involved when a conversion is done.
    Hiding or eliminating code isn't helpful in that case; the results can
    also be misleading:

    Take this example:

       void fred(void) {
        _Bool b;
          int i;
          i=b;
       }

    Unoptimised, it generates this code:

             push    rbp
             mov     rbp, rsp

             mov     al, byte ptr [rbp - 1]
             and     al, 1
             movzx   eax, al
             mov     dword ptr [rbp - 8], eax

             pop     rbp
             ret

    You can see from this that a Bool occupies one byte; it is masked to
    0/1 (so it doesn't trust it to contain only 0/1), then it is widened
    to an int size.

    With optimisation turned on, even at -O1, it produces this:

             ret

    That strikes me as rather less enlightening!

    Meanwhile your foo_b2i function contains this optimised code:

             mov     eax, edi
             ret

    The masking and widening is not present. Presumably, it is taking
    advantage of the fact that a _Bool argument will be converted and
    widened to `int` at the callsite even though the parameter type is
    also _Bool. So the conversion has already been done.

    You will see this if writing also a call to foo_b2i() and looking at
    the /non-elided/ code.

    The unoptimised code for foo_b2i is pretty awful (like masking twice,
    with a pointless write to memory between them). But sometimes with gcc
    there is no sensible middle ground between terrible code, and having
    most of it eliminated.

    The unoptimised code from my C compiler for foo_b2i, excluding
    entry/exit code, is:

         movsx   eax, byte [rbp + foo_b2i.x]

    My compiler assumes that a _Bool type already contains 0 or 1.




    If you are doing constant expression in your compiler, then you have the
    same problem (casts) I am solving in cake.

    For instance
    static_assert((unsigned char)1234 == 210);

    is already working in my cake. I had to simulate this cast.

    Previously, I was doing all computations with bigger types for constant expressions. Then I realize compile time  must work as the runtime.

    For constexpr the compiler does not accept initialization invalid types.
    for instance.

     constexpr char s = 12345;

    <source>:6:21: error: constexpr initializer evaluates to 12345 which is
    not exactly representable in type 'const char'
        6 |  constexpr char s = 12345;


    I am also checking all wraparound and overflow in constant expressions.
    I have a warning when the computed value is different from the math value.

    In my C compiler I have no constexpr (don't know why you got that
    impression). And I don't check that initialisers for integer types
    overflow their destination.

    This is because within the language in general:

    char c; int i;

    c = i;

    Such an assignment is not checked at runtime (and I don't know if this
    can be warned against, or if a runtime check can be added).

    This is just how C works: too-large values are silently truncated (there
    are worse aspects of the language, like being able to do `int i; (&i)[12345];`).

    But you are presumably superimposing a new stricter language on top. In
    the case, if my `c = i` assignment was not allowed, how do I get around
    that; by a cast?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Thu Aug 8 20:09:56 2024
    On 08/08/2024 18:58, David Brown wrote:
    On 08/08/2024 19:29, Bart wrote:
    On 08/08/2024 17:32, Michael S wrote:
    On Thu, 8 Aug 2024 14:23:44 +0100
    Bart <[email protected]> wrote:
    Try godbolt.org. Type in a fragment of code that does different kinds >>  >> of casts (it needs to be well-formed, so inside a function), and see
    what code is produced with different C compilers.
    ;
    Use -O0 so that the code isn't optimised out of existence, and so
    that you can more easily match it to the C ource.
    ;
    ;
    ;
    ;
    I'd recommend an opposite - use -O2 so the cast that does nothing
    optimized away.
    ;
    int foo_i2i(int x) { return (int)x; }
    int foo_u2i(unsigned x) { return (int)x; }
    int foo_b2i(_Bool x) { return (int)x; }
    int foo_d2i(double x) { return (int)x; }
    The OP is curious as to what's involved when a conversion is done.
    Hiding or eliminating code isn't helpful in that case; the results can
    also be misleading:

    Michael is correct - the OP should enable optimisation, precisely to
    avoid the issue you are concerned about.  Without optimisation, the
    results are misleading - they will only show things that are /not/
    involved in the conversion, swamping the useful results with code that
    messes about putting data on and off the stack.  When optimised
    compilation shows that no code is generated, it is a very clear
    indication that no operations are needed for the conversions in question
    - unoptimized code hides that.


    Take this example:

       void fred(void) {
        _Bool b;
          int i;
          i=b;
       }

    Unoptimised, it generates this code:

             push    rbp
             mov     rbp, rsp

             mov     al, byte ptr [rbp - 1]
             and     al, 1
             movzx   eax, al
             mov     dword ptr [rbp - 8], eax

             pop     rbp
             ret

    You can see from this that a Bool occupies one byte; it is masked to
    0/1 (so it doesn't trust it to contain only 0/1), then it is widened
    to an int size.


    No, you can't see that.  All you can see is garbage in, garbage out. You have to start with a function that has some meaning!

    Sorry but my function is perfectly valid. It's taking a Bool value and converting it to an int.

    Perhaps you don't understand x86 code? I'll tell you: it loads that /byte-sized/ value, masks it, and widens it to an int. I'm surprised you
    can't see that.

    But I suspect a long gaslighting session coming on, where you refute the evidence that everyone else can see!



    With optimisation turned on, even at -O1, it produces this:

             ret

    Try again with:

        int foo(bool x) { return x; }

        bool bar(int x) { return x; }

    Try it with -O0 and -O1, and then tell us which you think gives a
    clearer indication of the operations needed.

    Michael is wrong and so are you.

    If you want to know what casting from bool to int entails, then testing
    it via a function call like this is the wrong way to do it, since half
    of it depends on what happens when evaluating arguments at the call site.

    Especially if you let the compiler do what it likes, like using its
    knowledge of that call process, which is not displayed here in the
    optimised code of the function body.


    So I have some questions of you:

    * How exactly is a _Bool value (which occupies one byte) translated to a
    32-bit signed integer? What is involved?

    This is machine independent other than the sizes mentioned.

    Given your answer, how does it correlate with either:


    mov eax, edi ; from your test; both optimised code

    <nothing> ; from my test



    The advantage of unoptimised code is that it will contain everything
    that is normally involved; it doesn't throw anything away.

    It doesn't require convincing the compiler that you're doing something
    useful to avoid it eliminating most or all your code, or turning it
    something that is just plain misleading.

    That might be useful when compiling a huge production version of an app,
    but it is useless when trying to shed light on an isolated fragment of code.

    Look, just forget it, I'm not in the mood for another marathon subthread.

    So, what's involved in turning Bool to int? According to your examples
    with -O1: nothing. You just copy 32 bits unchanged from one to the
    other. Mildly surprising, but you are of course right, right?

    However, now *I* have a problem, figuring out why on earth C compiler
    does the conversion like this:

    movsx eax, byte [source]

    Because this must be wrong, right?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Thiago Adams on Thu Aug 8 22:41:47 2024
    On 08/08/2024 21:34, Thiago Adams wrote:
    On 08/08/2024 16:42, Keith Thompson wrote:
    Thiago Adams <[email protected]> writes:
    On 07/08/2024 17:00, Dan Purgert wrote:
    On 2024-08-07, Thiago Adams wrote:
    [...]
    How about floating point?
    Floating point is a huge mess, and has a few variations for
    encoding;
    though I think most C implementations use the one from the IEEE on 1985 >>>> (uh, IEEE754, I think?)

    I didn't specify properly , but my question was more about floating
    point registers. I think in this case they have specialized registers.

    Who is "they"?

    Some CPUs have floating-point registers, some don't.  C says nothing
    about registers.

    What exactly is your question?  Is it not already answered by reading
    the "Conversions" section of the C standard?



    This part is related with the previous question about the origins of
    integer promotions.

    We don't have "char" register or signed/unsigned register. But I believe
    we may have double and float registers. So float does not need to be converted to double.

    There is no specif question here, just trying to understand the
    rationally behind the conversions rules.

    The rules have little to do with concrete machines with registers.

    Your initial post showed come confusion about how conversions work. They
    are not performed 'in-place', any more than writing `a + 1` changes the
    value of `a`.

    Take:

    int a; double x;

    x = (double)a;

    The cast is implicit here but I've written it out to make it clear. My C compiler produces intermediate code like this before converting it to
    native code:

    push x r64 # r64 means float64
    fix r64 -> i32
    pop a i32

    I could choose to interprete this code just as it is. Then, in this
    execution model, there are no registers at all, only a stack that can
    hold data of any type.

    The 'fix' instruction pops the double value from the stack, converts it
    to int (which involves changing both the bit-pattern, and the
    bit-width), and pushes it back onto the stack.

    Registers come into it when running it directly on a real machine. But
    you seem more concerned with safety and correctness than performance, so there's probably no real need to look at actual generated native code.

    That'll just be confusing (especially if you follow the advice to
    generate only optimised code).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Thiago Adams on Fri Aug 9 00:36:56 2024
    On 08/08/2024 22:34, Thiago Adams wrote:
    On 08/08/2024 16:42, Keith Thompson wrote:
    Thiago Adams <[email protected]> writes:
    On 07/08/2024 17:00, Dan Purgert wrote:
    On 2024-08-07, Thiago Adams wrote:
    [...]
    How about floating point?
    Floating point is a huge mess, and has a few variations for
    encoding;
    though I think most C implementations use the one from the IEEE on 1985 >>>> (uh, IEEE754, I think?)

    I didn't specify properly , but my question was more about floating
    point registers. I think in this case they have specialized registers.

    Who is "they"?

    Some CPUs have floating-point registers, some don't.  C says nothing
    about registers.

    What exactly is your question?  Is it not already answered by reading
    the "Conversions" section of the C standard?



    This part is related with the previous question about the origins of
    integer promotions.

    We don't have "char" register or signed/unsigned register. But I believe
    we may have double and float registers. So float does not need to be converted to double.

    There is no specif question here, just trying to understand the
    rationally behind the conversions rules.



    Stop trying to think about registers or implementations until you have understood the meaning of the conversions, as detailed in the C
    standards. Implementation details are just that - implementation
    details, which vary from target to target, compiler to compiler, and
    according to the circumstances and the rest of the surrounding code.

    In general, conversions try to preserve values (with the exceptions and limitations detailed in the standards). If that involves changing the underlying bit representations, then those changes are made. If no
    changes are needed, no changes will be made.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Thiago Adams on Thu Aug 8 18:40:15 2024
    Thiago Adams <[email protected]> writes:
    ...
    I also curious about how bool works.

    Values converted to bool became 0 or 1.
    When this conversion happens, at read or write? Both?

    You can take a value obtained by reading an object, or a value produced
    by evaluating an expression, and convert that value to a different type.
    That value can later be stored in an object, or it could be used as one
    of the operands for an expression. The conversion isn't associated with
    either the read or the write. Many conversions occur implicitly, a cast
    is used to explicitly make a conversion occur.

    int x = 3;
    bool b = x;

    In the above code, an implicit conversion from int to bool occurs after
    reading the value of 3 from x, and occurs before writing to bool.

    b = !(bool)(x-3);

    In this code, the conversion occurs after the value of 3 is retrieved
    from x, and after 3 is subtracted from it. That result of 0 is then
    converted to bool, and then the ! operator is applied to it. Finally,
    the result is written to b. So you see, it doesn't make sense to connect
    the conversion with either the read or the write.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Fri Aug 9 00:32:00 2024
    On 08/08/2024 21:09, Bart wrote:
    On 08/08/2024 18:58, David Brown wrote:
    On 08/08/2024 19:29, Bart wrote:
    On 08/08/2024 17:32, Michael S wrote:
    On Thu, 8 Aug 2024 14:23:44 +0100
    Bart <[email protected]> wrote:
    Try godbolt.org. Type in a fragment of code that does different
    kinds
    of casts (it needs to be well-formed, so inside a function), and see >>>  >> what code is produced with different C compilers.
    ;
    Use -O0 so that the code isn't optimised out of existence, and so
    that you can more easily match it to the C ource.
    ;
    ;
    ;
    ;
    I'd recommend an opposite - use -O2 so the cast that does nothing
    optimized away.
    ;
    int foo_i2i(int x) { return (int)x; }
    int foo_u2i(unsigned x) { return (int)x; }
    int foo_b2i(_Bool x) { return (int)x; }
    int foo_d2i(double x) { return (int)x; }
    The OP is curious as to what's involved when a conversion is done.
    Hiding or eliminating code isn't helpful in that case; the results
    can also be misleading:

    Michael is correct - the OP should enable optimisation, precisely to
    avoid the issue you are concerned about.  Without optimisation, the
    results are misleading - they will only show things that are /not/
    involved in the conversion, swamping the useful results with code that
    messes about putting data on and off the stack.  When optimised
    compilation shows that no code is generated, it is a very clear
    indication that no operations are needed for the conversions in
    question - unoptimized code hides that.


    Take this example:

       void fred(void) {
        _Bool b;
          int i;
          i=b;
       }

    Unoptimised, it generates this code:

             push    rbp
             mov     rbp, rsp

             mov     al, byte ptr [rbp - 1]
             and     al, 1
             movzx   eax, al
             mov     dword ptr [rbp - 8], eax

             pop     rbp
             ret

    You can see from this that a Bool occupies one byte; it is masked to
    0/1 (so it doesn't trust it to contain only 0/1), then it is widened
    to an int size.


    No, you can't see that.  All you can see is garbage in, garbage out.
    You have to start with a function that has some meaning!

    Sorry but my function is perfectly valid. It's taking a Bool value and converting it to an int.

    No, it is not.

    Attempting to use the value of a non-static local variable that has not
    been initialised or assigned is undefined behaviour. Your function is
    garbage. No one can draw any conclusions about how meaningless code is compiled.


    Perhaps you don't understand x86 code? I'll tell you: it loads that /byte-sized/ value, masks it, and widens it to an int. I'm surprised you can't see that.


    I understand x86 well enough - perhaps not as well as you, but well
    enough. I do, however, understand C better than you, it seems. The x86
    code is irrelevant to the fact that your code has undefined behaviour.
    (And even if it were defined, it would still do nothing relevant.)

    But I suspect a long gaslighting session coming on, where you refute the evidence that everyone else can see!


    You are projecting.

    Look, it is extraordinarily simple to write functions that /actually/ do
    the conversions under discussion. Why waste time writing nonsense
    functions that do that?



    With optimisation turned on, even at -O1, it produces this:

             ret

    Try again with:

         int foo(bool x) { return x; }

         bool bar(int x) { return x; }

    Try it with -O0 and -O1, and then tell us which you think gives a
    clearer indication of the operations needed.

    Michael is wrong and so are you.

    If you want to know what casting from bool to int entails, then testing
    it via a function call like this is the wrong way to do it, since half kj
    of it depends on what happens when evaluating arguments at the call site.


    Nope.

    But if you prefer, just use external variables:

    int i;
    bool b;

    void to_int_0(void) { i = b; }
    void to_bool_0(void) { b = i; }

    <https://www.godbolt.org/z/eT9Y84Gx4>


    Again, look at the two functions with -O0 and -O1, and tell me which is clearer.

    Especially if you let the compiler do what it likes, like using its
    knowledge of that call process, which is not displayed here in the
    optimised code of the function body.


    So I have some questions of you:

    * How exactly is a _Bool value (which occupies one byte) translated to a 32-bit signed integer? What is involved?


    A _Bool is always either 0 or 1. The conversion is whatever the
    compiler needs to give an int of value 0 or 1.

    The implementation details depend entirely on the target. Typically, if
    the _Bool is in memory, then a single byte is read and zero-extended to
    the width of a register. If the _Bool is passed to a function as a
    parameter, it is usually already extended - but that will depend on the
    calling conventions.

    This is machine independent other than the sizes mentioned.


    The specification is independent - it is given by the C standards. The implementation is most certainly not machine independent. It is also
    not necessarily consistent - compilers can and do pick different code
    depending on the circumstances (where the _Bool came from, and how the
    int is going to be used). You are even wrong in stating that a _Bool
    occupies one byte, since that is not a requirement for a C
    implementation (though I don't know of any real-world C implementations
    with larger _Bool's).


    Given your answer, how does it correlate with either:


       mov eax,    edi     ; from your test; both optimised code


    Looks fine.

       <nothing>           ; from my test


    That's fine for the nonsense function you wrote.



    The advantage of unoptimised code is that it will contain everything
    that is normally involved; it doesn't throw anything away.


    No, it does not - because normal code generated by C compilers used by C programmers who want sensible results will be the result of compiling
    with optimisation, and will look very different.

    It doesn't require convincing the compiler that you're doing something
    useful to avoid it eliminating most or all your code, or turning it
    something that is just plain misleading.


    You have /never/ understood how to look at generated code, have you?

    That might be useful when compiling a huge production version of an app,
    but it is useless when trying to shed light on an isolated fragment of
    code.

    Look, just forget it, I'm not in the mood for another marathon subthread.


    OK. I expect the OP to understand these things better.

    So, what's involved in turning Bool to int? According to your examples
    with -O1: nothing. You just copy 32 bits unchanged from one to the
    other. Mildly surprising, but you are of course right, right?


    Yes, I am right - and I don't see that as even mildly surprising here.
    That's how you convert a _Bool in a register to an int in a register.
    You'll see equally little code when converting between signed and
    unsigned types, or various other integer types.

    However, now *I* have a problem, figuring out why on earth C compiler
    does the conversion like this:

        movsx eax, byte [source]

    Because this must be wrong, right?



    No. You are asking it to do something else - you are asking it to load
    a _Bool from memory, not just convert it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Fri Aug 9 01:56:15 2024
    On 08/08/2024 23:32, David Brown wrote:
    On 08/08/2024 21:09, Bart wrote:

    Sorry but my function is perfectly valid. It's taking a Bool value and
    converting it to an int.

    No, it is not.

    Attempting to use the value of a non-static local variable that has not
    been initialised or assigned is undefined behaviour.  Your function is garbage.  No one can draw any conclusions about how meaningless code is compiled.

    FFS. You really think that makes a blind bit of difference? A variable
    is not initialised, so the bool->int code shown must be just random
    rubbish generated by the compiler? You think I wouldn't spot if there
    was something amiss?

    OK, let's initialise it and see what difference it actually makes. My
    code is now this:

    #include <stdbool.h>

    void BC(void) {
    _Bool b;
    int i;
    i=b;
    }

    void DB(void) {
    _Bool b=false;
    int i;
    i=b;
    }

    The output from gcc -O1 is this:

    BC:
    ret
    DB:
    ret

    There is no difference. So, even initialised, it tells me nothing about
    what might be involved in bool->int conversion. It is useless.

    Now I'll try it with -O0 (line breaks added):

    BC:
    push rbp
    mov rbp, rsp

    movzx eax, BYTE PTR [rbp-1]
    mov DWORD PTR [rbp-8], eax

    nop
    pop rbp
    ret
    DB:
    push rbp
    mov rbp, rsp
    mov BYTE PTR [rbp-1], 0

    movzx eax, BYTE PTR [rbp-1]
    mov DWORD PTR [rbp-8], eax

    nop
    pop rbp
    ret

    Exactly the same code, except DB has an extra line to initialise that value.

    Are you surprised it is the same? I am 99% sure that you already knew
    this, but were pretending that the code was meaningless, for reasons
    that escape me.

    One more thing: the ASM code I posted earlier was from Clang 18.1, above
    it's from gcc 14.1.

    The Clang code masks bit 0 of the bool value; gcc doesn't.

    However, you can only know that by using -O0 in both cases. Using the
    -O1 or higher that you recommend, you only see this:

    BC:
    ret

    DB:
    ret

    for both compilers. That is utterly useless. YMMV.


    If you want to know what casting from bool to int entails, then
    testing it via a function call like this is the wrong way to do it,
    since half kj
    of it depends on what happens when evaluating arguments at the call site.


    Nope.

    So in:

    mov eax, ecx

    inside foo_b2i(), at what point did the 8-bit _Bool get turned into the
    32-bit value in ecx?

    That's fine for the nonsense function you wrote.

    Actually, MS (the poster) wrote those foo_* functions .




    The advantage of unoptimised code is that it will contain everything
    that is normally involved; it doesn't throw anything away.


    No, it does not - because normal code generated by C compilers used by C programmers who want sensible results will be the result of compiling
    with optimisation, and will look very different.

    As I said, this isn't production code of a real program. It is a single
    line. Elsewhere you had to resort to using statics (and linkage outside
    of a function) to stop code being eliminated out of existence.


    With -O0 you don't need such tricks, and don't need to think about what
    might have been removed that you really need to see.

    So, what's involved in turning Bool to int? According to your examples
    with -O1: nothing. You just copy 32 bits unchanged from one to the
    other. Mildly surprising, but you are of course right, right?


    Yes, I am right - and I don't see that as even mildly surprising here.
    That's how you convert a _Bool in a register to an int in a register.

    We don't know where the bool passed to foo_b2i came from; maybe it was
    an element of an array or struct, so it would have been widened at some
    point before calling the function. So that example would give a
    misleading picture of what's involved.


    No.  You are asking it to do something else - you are asking it to load
    a _Bool from memory, not just convert it.

    It loads from memory and converts it in one instruction; isn't that
    something? But this is pretty much what your godbolt link does, where
    you have to resort to using static variables, since for -O1 and above,
    locals are kept in registers:

    movzx eax, BYTE PTR b[rip]

    It seems there /is/ something I overlooked: the -S output of gcc/clang
    is less readable, since variable names disappear: they either are kept
    in registers (where you don't know which is which); or they are
    addressed by numeric offsets, and again you don't know what is what.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Keith Thompson on Fri Aug 9 02:47:08 2024
    On Thu, 08 Aug 2024 16:14:09 -0700, Keith Thompson wrote:

    The value of a _Bool object is always either 0 or 1 *unless* the program
    does something weird.

    If a variable is declared to be of a particular type, does that mean that
    any possible value of that variable is, by definition, interpreted as some instance of that type?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Fri Aug 9 02:45:43 2024
    On Thu, 8 Aug 2024 20:09:56 +0100, Bart wrote:

    But I suspect a long gaslighting session coming on, where you refute the evidence that everyone else can see!

    A denial is not a refutation.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Stefan Ram on Fri Aug 9 02:42:55 2024
    On 8 Aug 2024 12:44:46 GMT, Stefan Ram wrote:

    The whole casting concept hails from Algol.

    Type conversions are as old as the concept of types themselves.

    Algol 68 introduced the term “coercion” for explicit type conversions.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Lawrence D'Oliveiro on Fri Aug 9 02:08:04 2024
    On 8/8/24 22:47, Lawrence D'Oliveiro wrote:
    On Thu, 08 Aug 2024 16:14:09 -0700, Keith Thompson wrote:

    The value of a _Bool object is always either 0 or 1 *unless* the program
    does something weird.

    If a variable is declared to be of a particular type, does that mean that
    any possible value of that variable is, by definition, interpreted as some instance of that type?

    No, modulo some uncertainties about what precisely you mean by "possible value". I'm assuming that you mean object representations that can be
    created by your code.

    Every object type has a range of representable values. For each of those representable values there's one or more object representations that
    represent that value. Writing a representable value to an object using
    code with defined behavior and an lvalue of a given type always results
    in a valid object representation according to that type.

    However, there can be, and often are, object representations that do not represent a value when interpreted according to the type of the lvalue
    used to read them. These are called non-value representations. This can
    only happen as a result of type-punning or code that has undefined behavior.

    Here's what the standard says about such situations:
    "If such a representation is read by an lvalue expression that does not
    have character type, the behavior is undefined. If such a representation
    is produced by a side effect that modifies all or any part of the object
    by an lvalue expression that does not have character type, the behavior
    is undefined.55)" (6.2.6.1p5)

    So, no, not all object representations represent valid values of the
    type used to access them, and it's a very bad thing when you let that
    happen.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Fri Aug 9 11:04:35 2024
    On 09/08/2024 00:17, Keith Thompson wrote:
    Bart <[email protected]> writes:
    [...]
    Take:

    int a; double x;

    x = (double)a;

    The cast is implicit here but I've written it out to make it clear.

    [...]

    The *conversion* could be done implicitly, but you've used a cast (i.e.,
    an explicit conversion) to make it clear.

    There is no such thing as an "implicit cast" in C.


    Suppose I write this code:

    x = a; // implicit 'conversion'
    x = (double)a; // explicit 'conversion'


    My compiler produces these two bits of AST for the RHS of both expressions:

    1 00009 r64---|---2 convert: sfloat_c i32 => r64
    1 00009 i32---|---|---1 name: t.main.a.1

    1 00010 r64---|---2 convert: sfloat_c i32 => r64
    1 00010 i32---|---|---1 name: t.main.a.1

    So whatever you call that `(double)` part of the second line, which is
    written explicitly, exactly the same thing is done internally (ie
    'implicitly') to the first line. (The 09/10 are line numbers.)

    Since C likes to use the term 'cast' for such conversions, I don't see a problem with talking about implicit and explicit versions.

    It just seems to irk the pedantics here.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Thiago Adams on Fri Aug 9 16:25:41 2024
    On 09/08/2024 11:57, Thiago Adams wrote:
    Em 8/8/2024 6:41 PM, Bart escreveu:
    On 08/08/2024 21:34, Thiago Adams wrote:
    On 08/08/2024 16:42, Keith Thompson wrote:
    Thiago Adams <[email protected]> writes:
    On 07/08/2024 17:00, Dan Purgert wrote:
    On 2024-08-07, Thiago Adams wrote:
    [...]
    How about floating point?
    Floating point is a huge mess, and has a few variations for
    encoding;
    though I think most C implementations use the one from the IEEE on >>>>>> 1985
    (uh, IEEE754, I think?)

    I didn't specify properly , but my question was more about floating
    point registers. I think in this case they have specialized registers. >>>>
    Who is "they"?

    Some CPUs have floating-point registers, some don't.  C says nothing
    about registers.

    What exactly is your question?  Is it not already answered by reading >>>> the "Conversions" section of the C standard?



    This part is related with the previous question about the origins of
    integer promotions.

    We don't have "char" register or signed/unsigned register. But I
    believe we may have double and float registers. So float does not
    need to be converted to double.

    There is no specif question here, just trying to understand the
    rationally behind the conversions rules.

    The rules have little to do with concrete machines with registers.

    Your initial post showed come confusion about how conversions work.
    They are not performed 'in-place', any more than writing `a + 1`
    changes the value of `a`.

    Take:

         int a; double x;

         x = (double)a;

    The cast is implicit here but I've written it out to make it clear. My
    C compiler produces intermediate code like this before converting it
    to native code:

         push x   r64                   # r64 means float64
         fix      r64 -> i32
         pop  a   i32

    I could choose to interprete this code just as it is. Then, in this
    execution model, there are no registers at all, only a stack that can
    hold data of any type.

    The 'fix' instruction pops the double value from the stack, converts
    it to int (which involves changing both the bit-pattern, and the
    bit-width), and pushes it back onto the stack.

    Registers come into it when running it directly on a real machine. But
    you seem more concerned with safety and correctness than performance,
    so there's probably no real need to look at actual generated native code.

    That'll just be confusing (especially if you follow the advice to
    generate only optimised code).




    This part was always clear to me:

    "They  are not performed 'in-place', any more than writing `a + 1`
    changes the value of `a`."

    Lets take double to int.

    In this case the bits of double needs to be reinterpreted (copied to) int.

    So the answer "how it works" can be

    always/generally machine has a instruction to do this

    If it supports those types in hardware, then it will probably have
    conversion instructions.

    But I've also used machines without FP hardware, so it had to be
    emulated in software. Then such a conversion would be done by a function
    in the runtime library.

    (Typically, also, such a machine had a FP type which might have been
    twice the width of a machine word, eg. `int` was `i16`, and `float` was
    `f32`. So those functions had to deal with that too.

    Currently we're in a golden age where nearly everything is 64 bits, and
    both 64-bit ints and floats suffice for most purposes. So that aspect is
    no longer an issue.

    Other than you now have to juggle 4 integer widths instead of just 2 or 3.)




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Fri Aug 9 18:16:19 2024
    On 09/08/2024 01:14, Keith Thompson wrote:
    David Brown <[email protected]> writes:
    [...]
    A _Bool is always either 0 or 1. The conversion is whatever the
    compiler needs to give an int of value 0 or 1.

    The value of a _Bool object is always either 0 or 1 *unless* the program
    does something weird.

    True. But attempting to use a _Bool object (as a _Bool) that does not
    contain either 0 or 1 is going to be undefined behaviour (at least it
    was on the platform where I saw this happen as a code bug).


    C23 is a bit clearer about the representation of bool (still also called _Bool) than previous editions. It states (draft N3220) that :

    The type bool shall have one value bit and (sizeof(bool)*CHAR_BIT)-1
    padding bits.

    There are several ways to force a representation other than 00000000 or 00000001 into a _Bool object, including a union, memset(), or type
    punning via a pointer cast.

    C23 dropped the term "trap representation", replacing it with "non-value representation" -- a reasonable change, since accessing a trap
    representation is not guaranteed to cause the program to "perform a
    trap" (defined as "interrupt execution of the program such that no
    further operations are performed").

    It doesn't specify whether setting the padding bits to 1 results in a non-value representation.

    That's probably an implementation-defined issue, is it not?


    If non-zero padding bits create a non-value representation, then
    accessing a bool object holding such a representation has undefined
    behavior. It could, among other things, yield the same value implied by
    the representation as if it were an ordinary integer of the same size.

    If there are no non-value representations, then only the
    single value bit determines the value, which is either false or true.

    As you mentioned, I expect that sizeof (bool) will normally be 1, but an implementation could make it wider, e.g. with 1 value bit and 31 padding bits.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Fri Aug 9 19:12:47 2024
    On 09/08/2024 12:04, Bart wrote:
    On 09/08/2024 00:17, Keith Thompson wrote:
    Bart <[email protected]> writes:
    [...]
    Take:

         int a; double x;

         x = (double)a;

    The cast is implicit here but I've written it out to make it clear.

    [...]

    The *conversion* could be done implicitly, but you've used a cast (i.e.,
    an explicit conversion) to make it clear.

    There is no such thing as an "implicit cast" in C.


    Suppose I write this code:

        x = a;                  // implicit 'conversion'
        x = (double)a;          // explicit 'conversion'


    My compiler produces these two bits of AST for the RHS of both expressions:

    1 00009 r64---|---2 convert: sfloat_c i32 => r64
    1 00009 i32---|---|---1 name: t.main.a.1

    1 00010 r64---|---2 convert: sfloat_c i32 => r64
    1 00010 i32---|---|---1 name: t.main.a.1

    So whatever you call that `(double)` part of the second line, which is written explicitly, exactly the same thing is done internally (ie 'implicitly') to the first line. (The 09/10 are line numbers.)


    You've written it yourself. Both are conversions - one is implicit, the
    other is explicit.

    Since C likes to use the term 'cast' for such conversions, I don't see a problem with talking about implicit and explicit versions.


    C does not "like" to use the term "cast" for anything other than cast operations, as defined by the C standards. Implicit conversions are not
    casts.

    /You/ might like to call implicit conversions "casts", but you'd be
    wrong to do so.

    It just seems to irk the pedantics here.

    You mean, people who know what they are talking about rather than those
    that make up stuff as they go along?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Fri Aug 9 19:08:42 2024
    On 09/08/2024 02:56, Bart wrote:
    On 08/08/2024 23:32, David Brown wrote:
    On 08/08/2024 21:09, Bart wrote:

    Sorry but my function is perfectly valid. It's taking a Bool value
    and converting it to an int.

    No, it is not.

    Attempting to use the value of a non-static local variable that has
    not been initialised or assigned is undefined behaviour.  Your
    function is garbage.  No one can draw any conclusions about how
    meaningless code is compiled.

    FFS. You really think that makes a blind bit of difference?

    Yes, I do.

    A variable
    is not initialised, so the bool->int code shown must be just random
    rubbish generated by the compiler? You think I wouldn't spot if there
    was something amiss?

    You wrote C code that had something amiss!

    How often are you going to do this? Write some piece of meaningless
    crap with undefined behaviour or - at best - no observable behaviour at
    all, compile with silly choices of flags, and then make nonsensical
    claims about what compilers do or how C is defined? How hard is it for
    you to actually write a C function that has defined behaviour that does
    what you want it to do? Get step 1 of these tests right, and we won't
    have to keep repeating this stuff.


    OK, let's initialise it and see what difference it actually makes. My
    code is now this:

      #include <stdbool.h>

      void BC(void) {
          _Bool b;
          int i;
          i=b;
      }

      void DB(void) {
          _Bool b=false;
          int i;
          i=b;
      }

    The output from gcc -O1 is this:

    BC:
            ret
    DB:
            ret

    There is no difference.

    There is a difference in the code. One (BC) has no defined behaviour.
    The other (DB) has defined behaviour with no observable behaviour. It's
    not a surprise that a compiler generates no code for either of them.

    So, even initialised, it tells me nothing about
    what might be involved in bool->int conversion. It is useless.


    Agreed. Nobody suggested your code above as a good idea.

    Now I'll try it with -O0 (line breaks added):

    BC:
            push    rbp
            mov     rbp, rsp

            movzx   eax, BYTE PTR [rbp-1]
            mov     DWORD PTR [rbp-8], eax

            nop
            pop     rbp
            ret
    DB:
            push    rbp
            mov     rbp, rsp
            mov     BYTE PTR [rbp-1], 0

            movzx   eax, BYTE PTR [rbp-1]
            mov     DWORD PTR [rbp-8], eax

            nop
            pop     rbp
            ret

    Exactly the same code, except DB has an extra line to initialise that
    value.

    Are you surprised it is the same? I am 99% sure that you already knew
    this, but were pretending that the code was meaningless, for reasons
    that escape me.


    Instead of trolling with what you know, without doubt, are pointless
    straw men, why not apply a little thought and write functions that make
    sense? Or look at the functions that I wrote?

    The stuff you write is either meaningless, or pointless, or perhaps
    both. I have no doubt that you know this fine.

    So the big question is, why are you writing it? Is it just to provoke
    me? Is it because you want to confuse the OP and other readers? Do you
    like pretending to be a fool?

    I'm giving up trying to help you - at least until you show some hint of
    trying to learn. I will still make posts pointing out when you write
    nonsense that might confuse or mislead others, but I'll stop trying to
    explain things unless you specifically ask.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Bart on Fri Aug 9 13:57:59 2024
    On 09/08/2024 12:04, Bart wrote:
    On 09/08/2024 00:17, Keith Thompson wrote:
    ...
    There is no such thing as an "implicit cast" in C.


    Suppose I write this code:

        x = a;                  // implicit 'conversion'
        x = (double)a;          // explicit 'conversion'


    My compiler produces these two bits of AST for the RHS of both expressions:

    1 00009 r64---|---2 convert: sfloat_c i32 => r64
    1 00009 i32---|---|---1 name: t.main.a.1

    1 00010 r64---|---2 convert: sfloat_c i32 => r64
    1 00010 i32---|---|---1 name: t.main.a.1

    Of course - an implicit conversion has exactly the same effect as a
    explicit conversion, if the source and destination types are the same.
    That doesn't make it correct to use the term "cast" to describe anything
    other than an explicit conversion.

    So whatever you call that `(double)` part of the second line, which is written explicitly, exactly the same thing is done internally (ie 'implicitly') to the first line. (The 09/10 are line numbers.)

    Since C likes to use the term 'cast' for such conversions, ...

    No, C only uses the term "cast" to describe the following:

    "6.5.4 Cast operators
    1 cast-expression:
    unary-expression
    ( type-name ) cast-expression" (6.5.4p1)

    A cast is a piece of syntax that is used to explicitly request that a conversion be performed. Conversions that are explicitly requested in C
    code are referred to as casts only by people who don't understand what
    they're saying - the standard never refers to them as such.

    ... I don't see a
    problem with talking about implicit and explicit versions.

    There's nothing wrong with talking about implicit conversions versus
    explicit conversions. Explicit conversion are also called casts.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Thiago Adams on Fri Aug 9 19:20:27 2024
    On 09/08/2024 12:57, Thiago Adams wrote:
    Em 8/8/2024 6:41 PM, Bart escreveu:
    On 08/08/2024 21:34, Thiago Adams wrote:
    On 08/08/2024 16:42, Keith Thompson wrote:
    Thiago Adams <[email protected]> writes:
    On 07/08/2024 17:00, Dan Purgert wrote:
    On 2024-08-07, Thiago Adams wrote:
    [...]
    How about floating point?
    Floating point is a huge mess, and has a few variations for
    encoding;
    though I think most C implementations use the one from the IEEE on >>>>>> 1985
    (uh, IEEE754, I think?)

    I didn't specify properly , but my question was more about floating
    point registers. I think in this case they have specialized registers. >>>>
    Who is "they"?

    Some CPUs have floating-point registers, some don't.  C says nothing
    about registers.

    What exactly is your question?  Is it not already answered by reading >>>> the "Conversions" section of the C standard?



    This part is related with the previous question about the origins of
    integer promotions.

    We don't have "char" register or signed/unsigned register. But I
    believe we may have double and float registers. So float does not
    need to be converted to double.

    There is no specif question here, just trying to understand the
    rationally behind the conversions rules.

    The rules have little to do with concrete machines with registers.

    Your initial post showed come confusion about how conversions work.
    They are not performed 'in-place', any more than writing `a + 1`
    changes the value of `a`.

    Take:

         int a; double x;

         x = (double)a;

    The cast is implicit here but I've written it out to make it clear. My
    C compiler produces intermediate code like this before converting it
    to native code:

         push x   r64                   # r64 means float64
         fix      r64 -> i32
         pop  a   i32

    I could choose to interprete this code just as it is. Then, in this
    execution model, there are no registers at all, only a stack that can
    hold data of any type.

    The 'fix' instruction pops the double value from the stack, converts
    it to int (which involves changing both the bit-pattern, and the
    bit-width), and pushes it back onto the stack.

    Registers come into it when running it directly on a real machine. But
    you seem more concerned with safety and correctness than performance,
    so there's probably no real need to look at actual generated native code.

    That'll just be confusing (especially if you follow the advice to
    generate only optimised code).




    This part was always clear to me:

    "They  are not performed 'in-place', any more than writing `a + 1`
    changes the value of `a`."

    Lets take double to int.

    In this case the bits of double needs to be reinterpreted (copied to) int.

    So the answer "how it works" can be

    always/generally machine has a instruction to do this

    or.. this is defined by the IIE ... standard as ...



    It would be helpful if you made more of an effort to write clearly here.
    (We know you can do so when you want to.) It is very difficult to
    follow what you are referring to here - what is "this case" here? A
    conversion from a double to an int certainly does not re-interpret or
    copy bits - like other conversions, it copies the /value/ to the best
    possible extent given the limitations of the types.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Thiago Adams on Fri Aug 9 21:43:44 2024
    On 09/08/2024 21:05, Thiago Adams wrote:
    and I am still curious if _Bool/bool makes the programs slower (more instructions) compared with
    "typedef int bool" because the generated code has to convert bool->int int->bool all the time.


    I'll answer this one first - the answer is, it depends. Some things
    will be faster, some slower, some stay much the same. Overall, expect
    proper booleans to be somewhat more efficient.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Thiago Adams on Fri Aug 9 22:01:22 2024
    On 09/08/2024 20:54, Thiago Adams wrote:

    Everything is a bit mixed up, but I'll try to explain the part about registers that I have in mind.

    In C, when you have an expression like char + char, each char is
    promoted to int. The computation then occurs as int + int.


    Yes. In C, there are no arithmetic operations on types smaller than "int".

    On the other hand, when you have float + float, it remains as float +
    float.


    Yes.

    The rules for promotions are quite clear in the standards. You can also
    read about them here: <https://en.cppreference.com/w/c/language/conversion>

    My guess for this design is that computations involving char are done
    using registers that are the size of an int.


    It is quite possible that this was the original motivation.

    But you keep referring to "the computer". There is no "the computer" in
    C. There are processors with 128 64-bit integer registers, and
    processors with a single 8-bit register. Some have no floating point
    hardware at all, some have 128-bit floating point hardware. C is
    defined in a manner that is mostly independent of the hardware, with
    only a few points that are dependent (such as the width of integer
    types). But design decisions in C can certainly have been inspired by
    real existing hardware.


    But, float + float is not promoted to double, so I assume that the
    computer has specific float registers or similar operation instructions
    for float.

    Regarding the part about signed/unsigned registers and operations, I
    must admit that I'm not sure. I was planning to check on Compiler
    Explorer, but I haven't done that yet.


    I don't know what you are referring to here. But if you are using
    compiler explorer, I encourage you to look at the generated output for a
    wide range of targets, including 8-bit AVR, 16-bit MSP430, 32-bit ARM,
    and 64-bit x86. Use gcc -O1 or -O2 in every case. (Ignore Bart's
    ignorant blatherings about optimisation.)


    I can frame the question like this: Does the computer make a distinction
    when adding signed versus unsigned integers? Are there specific assembly instructions for signed versus unsigned operations, covering all
    possible combinations?


    Without specifying "the computer", the question is not particularly
    meaningful. However, it's fair to say that on most processors most
    arithmetic operations are the same for signed and unsigned types as long
    as the operation is done at a size that the target supports (otherwise
    it may need sign or zero extensions if it only supports larger sizes).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Fri Aug 9 21:31:23 2024
    On 09/08/2024 20:23, Keith Thompson wrote:
    Bart <[email protected]> writes:
    [...]
    Since C likes to use the term 'cast' for such conversions,

    C never uses the term "cast" to refer to implicit conversions.

    I don't see
    a problem with talking about implicit and explicit versions.

    It just seems to irk the pedantics here.

    That does seem to be your goal.


    With:

    x = (double)a;

    The cast is implicit here but I've written it out to make it clear.

    I was replying to Thiago Adams.

    I didn't choose to muddy the waters. Dragging C standard legalese really doesn't help here.

    (90% of the discussion here could be shut down with people refered to
    the C standard. What else is needed to answer nearly all questions asked?

    Could it possibly be a more human, informal element?)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to James Kuyper on Fri Aug 9 21:59:44 2024
    On 09/08/2024 18:57, James Kuyper wrote:
    On 09/08/2024 12:04, Bart wrote:


    A cast is a piece of syntax that is used to explicitly request that a conversion be performed. Conversions that are explicitly requested in C
    code are referred to as casts only by people who don't understand what they're saying - the standard never refers to them as such.

    Are you sure? What else would they be known as?


    ... I don't see a
    problem with talking about implicit and explicit versions.

    There's nothing wrong with talking about implicit conversions versus
    explicit conversions.

    Of course! Because those terms happen to be used in a couple of places
    in the standard. Usage of any terms not appearing in the standard is
    apparently outlawed in this newsgroup.

    Explicit conversion are also called casts.


    "6.5.4p3

    Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast."

    Here it uses the term 'explicit cast'. Why is that; isn't the term
    'cast' unambiguous without needing to say 'explicit'?

    Also, what is exactly is the difference between 'explicit conversion'
    and 'explicit cast'?

    Why can't there also be a similar correlation between 'implicit
    conversion' and 'implicit cast'? The only reason I can see is that out
    of these four terms, only 3 of them happen to appear in the standard.

    I don't see that as a compelling reason why that term should be
    considered absolutely wrong; 'implicit cast' just never came up.

    It's not as though the standard provides an official glossary, but even
    if it did, surely people ought to be allowed to use alternate terms for
    an informal discussion? This is a not a committee meeting.

    I remember people here getting castigated for using the term 'type
    cast'. And yet, in H.2.4p1:

    "The LIA−1 type conversions are the following type casts:"


    Look also at H.2.4p4 (also p5):

    "C’s conversions (casts) from floating-point to floating-point can meet LIA−1 requirements if an implementation uses round-to-nearest (IEC 60559 default)."

    Here it clearly indicates that 'conversions' (presumably both implicit
    and explicit) are also known as 'casts'.

    This seems to imply (no pun) that 'implicit casts' are a thing; the
    opportunity to use that term just never came up.

    I don't wish to be rude to you or KT but .....

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Fri Aug 9 22:01:37 2024
    On 09/08/2024 21:49, Keith Thompson wrote:
    Bart <[email protected]> writes:
    [...]
    I didn't choose to muddy the waters.

    Yes you did.

    Dragging C standard legalese
    really doesn't help here.

    Yes it does.

    You seem to believe, or you're pretending to believe, that using
    terminology incorrectly is better than using it correctly, even when
    using to correctly is just as easy.


    See my reply to JK.

    I don't think it's me who's trying to confuse by banning terms what
    everyone understands perfectly well.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to James Kuyper on Fri Aug 9 21:30:33 2024
    On 2024-08-09, James Kuyper <[email protected]> wrote:
    On 09/08/2024 12:04, Bart wrote:
    On 09/08/2024 00:17, Keith Thompson wrote:
    ...
    There is no such thing as an "implicit cast" in C.


    Suppose I write this code:

        x = a;                  // implicit 'conversion'
        x = (double)a;          // explicit 'conversion'


    My compiler produces these two bits of AST for the RHS of both expressions: >>
    1 00009 r64---|---2 convert: sfloat_c i32 => r64
    1 00009 i32---|---|---1 name: t.main.a.1

    1 00010 r64---|---2 convert: sfloat_c i32 => r64
    1 00010 i32---|---|---1 name: t.main.a.1

    Of course - an implicit conversion has exactly the same effect as a
    explicit conversion, if the source and destination types are the same.
    That doesn't make it correct to use the term "cast" to describe anything other than an explicit conversion.

    That's all very neat and clean. However, the problem is that in C,
    some of the implicit conversions are unsafe.

    Implicit conversions can:

    - truncate an integer value to fit a narrower type.

    - convert between floating point and integer in a way that the value
    is out of range, with undefined behavior ensuing.

    - change the value: e.g -1 becomes UINT_MAX.

    - subvert the type system, e.g. (foo *) -> (void *) -> (bar *).

    In computer science, we refer to unsafe conversion as coercion.
    THe cast notation is C's coercion operation.

    In some languages, some of what C allows to be an implicit conversion
    would be classified as requiring a coercion.

    It almost makes sense to speak of "implicit cast" (i.e. coercion) in C,
    because of what happens implicitly being so unsafe.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @[email protected]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Keith Thompson on Fri Aug 9 18:35:12 2024
    On 8/9/24 17:29, Keith Thompson wrote:
    James Kuyper <[email protected]> writes:
    [...]
    A cast is a piece of syntax that is used to explicitly request that a
    conversion be performed. Conversions that are explicitly requested in C
    code are referred to as casts only by people who don't understand what
    they're saying - the standard never refers to them as such.

    I think you omitted a "not" in the above, or meant to write "implicitly" rather than "explicitly".

    The latter - sorry for the confusion!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Bart on Fri Aug 9 18:29:02 2024
    Bart <[email protected]> writes:
    On 09/08/2024 18:57, James Kuyper wrote:
    ...
    A cast is a piece of syntax that is used to explicitly request that
    a
    conversion be performed. Conversions that are explicitly requested in C
    code are referred to as casts only by people who don't understand what
    they're saying - the standard never refers to them as such.

    Are you sure? What else would they be known as?

    As Keith said, that's a typo - the second "explicitly" should have been "implicitly".

    [...]
    Here it uses the term 'explicit cast'. Why is that; isn't the term
    'cast' unambiguous without needing to say 'explicit'?

    It's redundant, and occurs only once in the entire standard. The purpose
    of that redundancy was to emphasize that what the conversion it
    describes never happens implicitly (unlike many of the other conversions).

    Also, what is exactly is the difference between 'explicit conversion'
    and 'explicit cast'?

    None

    Why can't there also be a similar correlation between 'implicit
    conversion' and 'implicit cast'?

    The C standard defines "implicit conversion" and "explicit conversion"
    in 6.3p1, and the definition it provides for "explicit conversion" is
    "those [conversions] that result from a cast operation". it provides a
    grammar production for a cast expression, and none for a implicit cast expression.

    even if it did, surely people ought to be allowed to use alternate
    terms for an informal discussion? This is a not a committee meeting.

    Every time you use a term with a standard-define meaning in a way that
    doesn't match the meaning defined for it by the standard, you create
    potential confusion. If that's what you want to do, go ahead, but it
    seems an odd thing to do.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Fri Aug 9 23:14:48 2024
    On 2024-08-09, Keith Thompson <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:
    [...]
    It almost makes sense to speak of "implicit cast" (i.e. coercion) in C,
    because of what happens implicitly being so unsafe.

    I disagree, because that's not what "cast" means.

    "cast" means to (try to) project a value into another type.

    In C though, the nuance is something like "conversion that is mediated
    by the presence of the cast notation", where "mediated" includes the possibility that the cast notation has no effect at all
    (e.g. 2 + (int) 3).

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @[email protected]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Kaz Kylheku on Fri Aug 9 18:35:52 2024
    On 8/9/24 17:30, Kaz Kylheku wrote:
    On 2024-08-09, James Kuyper <[email protected]> wrote:
    ...
    Of course - an implicit conversion has exactly the same effect as a
    explicit conversion, if the source and destination types are the same.
    That doesn't make it correct to use the term "cast" to describe anything
    other than an explicit conversion.

    That's all very neat and clean. However, the problem is that in C,
    some of the implicit conversions are unsafe.

    Implicit conversions can:

    - truncate an integer value to fit a narrower type.

    - convert between floating point and integer in a way that the value
    is out of range, with undefined behavior ensuing.

    - change the value: e.g -1 becomes UINT_MAX.

    - subvert the type system, e.g. (foo *) -> (void *) -> (bar *).
    The implicit conversions are implicit precisely because they tend to be
    safer than the ones that cannot be done implicitly. That doesn't mean
    that they're safe.

    In computer science, we refer to unsafe conversion as coercion.
    THe cast notation is C's coercion operation.

    Yes, and in the C standard, conversions are simply called conversions, regardless of how unsafe they are - for instance, you can't get much
    less safe than (void (*))printf; but it's still a conversion. It's
    confusing to discuss C using terms that have a standard-defined meaning,
    if you insist on using them with a conflicting meaning.

    It almost makes sense to speak of "implicit cast" (i.e. coercion) in C, because of what happens implicitly being so unsafe.

    No, it doesn't. If lack of safety is what makes them implicit, then
    casts should be even more implicit than implicit conversions.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Sat Aug 10 01:11:08 2024
    On 10/08/2024 00:58, Keith Thompson wrote:

    In any case, while there may be some ambiguity about whether all casts specify conversions, it is unambiguous that an implicit conversion is
    not a cast.


    Here are some comments from the Tiny C source code:

    /* XXX: implicit cast ? */
    /* compute bigger type and do implicit casts */

    This is from some gcc code:

    ..and some compilers cast it to int implicitly ...

    Come on, everybody's at it. Unless we're trying do to a reference
    document, then /it really doesn't matter/.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Sat Aug 10 00:06:10 2024
    On 2024-08-09, Keith Thompson <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:
    On 2024-08-09, Keith Thompson <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:
    [...]
    It almost makes sense to speak of "implicit cast" (i.e. coercion) in C, >>>> because of what happens implicitly being so unsafe.

    I disagree, because that's not what "cast" means.

    "cast" means to (try to) project a value into another type.

    *Looks around* sorry, are we still in comp.lang.c?

    "Cast" has a number of meanings in contexts outside C, applicable to
    dice, eyes, fishing lines, ballots, magic spells, actors, liquid metal,
    and broken limbs, among other things. In C, it means what the standard
    says it means, even if some people misuse it to refer to implicit conversions.

    In C though, the nuance is something like "conversion that is mediated
    by the presence of the cast notation", where "mediated" includes the
    possibility that the cast notation has no effect at all
    (e.g. 2 + (int) 3).

    I hadn't noticed before that the standard does have a formal definition
    of the term "cast", as well as "explicit conversion" and "implicit conversion".

    Based on surveying all you quoted, I basically nailed it above.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @[email protected]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Sat Aug 10 00:32:39 2024
    On 09/08/2024 22:47, Keith Thompson wrote:
    Bart <[email protected]> writes:
    On 09/08/2024 18:57, James Kuyper wrote:
    On 09/08/2024 12:04, Bart wrote:

    A cast is a piece of syntax that is used to explicitly request that
    a
    conversion be performed. Conversions that are explicitly requested in C
    code are referred to as casts only by people who don't understand what
    they're saying - the standard never refers to them as such.

    Are you sure? What else would they be known as?

    I believe James made a small error here, either omitting a "not" or accidentally writing "explicitly" rather than "implicitly".

    With that correction, I presume what James wrote is clear enough.
    I ask you not to pretend that you still don't understand it.

    I guessed it was some sort of mistake. That's why I politely asked if he
    was sure.

    I ask you not to pretend that you still don't understand it.

    I might also ask you not to pretend you don't know what is meant by
    'implicit cast'. Or worse, pretending that other people might be confused.

    I'm sure most of those won't have gone anywhere near the standard, so
    they won't be aware that in the 700 pages of N1570.PDF, 'explicit cast'
    occurs all of once, while 'implicit cast' occurs one time less often,
    which appears to be the sole reason for have a go at anyone who commits
    the sin of using that expression.


    [...]
    Here it uses the term 'explicit cast'. Why is that; isn't the term
    'cast' unambiguous without needing to say 'explicit'?

    It's redundant.

    OK. Does that mean we're allowed to use redundant terms too?

    Also, what is exactly is the difference between 'explicit conversion'
    and 'explicit cast'?

    The both mean the same thing in C, but "explicit cast" is redundant.

    Why can't there also be a similar correlation between 'implicit
    conversion' and 'implicit cast'? The only reason I can see is that out
    of these four terms, only 3 of them happen to appear in the standard.

    Because a cast is an explicit operator.

    It's something that is done in the code to alter the evaluation of some expression. An 'explicit cast', which has to be requested in the source
    code, necessarily has some syntax associated with it.

    An 'implicit cast' (by which I mean, for your benefit as it puzzles
    nobody else, implicit type conversion), obviously /doesn't/ have any
    relevant syntax! If it had, then it would be explicit...

    I don't see that as a compelling reason why that term should be
    considered absolutely wrong; 'implicit cast' just never came up.

    It's not as though the standard provides an official glossary,

    In fact it does. Some terms are defined in section 3, and others are
    defined elsewhere in the standard (definitions are denoted by italics).
    I don't see a definition for the term "cast", but it's clearly described
    in N3220 6.5.4 "Cast operators".

    Array indexing is defined in terms of pointer arithmetic. The
    evaluation of `a[i]` involves an implicit addition operation. It does
    not involve an implicit "+" symbol.

    You've snipped my quote from H.2.4p5 where it says:

    ... conversions (casts) ...

    Here they are also 'mixing up' operations and syntax.

    Here's the signature of a function from my C compiler which deals with conversions:

    func docast(unit p, int t, hard=1)unit =

    (A 'unit' is an AST mode; 't' is a type code.) 'hard' is 1 for an
    explicit conversion, and 0 for an implicit one.

    (Explicit or 'hard' casts enable some conversions that would be
    otherwise be invalid. And in that last sentence, I used both 'cast' and 'conversion' just to avoid using the same term in quick succession. I'm
    sure many of the word choices in the standard are for similar reasons.)

    You're allowed to write whatever nonsense you like, and the rest of us
    are allowed to tell you that you're wrong.

    And some of us are allowed to think or say that you're being hopelessly pedantic.


    Explain why using the word "cast" incorrectly is better than using it correctly. Explain why you can't just refer to explicit and implicit conversions. Do you have a motivation other than being annoying?

    'Cast' is shorter, and more directly is associated with conversions to
    do with types.

    In my compiler for example, '*conv*' is used in many different contexts
    (eg. case conversion). '*cast*' is only used in two functions, both to
    do with C type conversions.

    But I also use both terms just to mix it up.

    I remember people here getting castigated for using the term 'type
    cast'. And yet, in H.2.4p1:

    "The LIA−1 type conversions are the following type casts:"

    That wording is no longer there in more recent editions. Annex H refers
    to LIA-1 (Language Independent Arithmetic), ISO/IEC 10967–1; perhaps
    that standard use the term "type casts".

    My point is that even the professionals who write such documents get it 'wrong', according to you.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Kaz Kylheku on Fri Aug 9 20:31:44 2024
    On 8/9/24 20:06, Kaz Kylheku wrote:
    On 2024-08-09, Keith Thompson <[email protected]> wrote:
    Kaz Kylheku <[email protected]> writes:
    ...
    "cast" means to (try to) project a value into another type.
    ...
    I hadn't noticed before that the standard does have a formal definition
    of the term "cast", ...

    Neither had I - noticing that earlier would have shortened this
    discussion (I hope).

    That definition is:
    "Preceding an expression by a parenthesized type name converts the value
    of the expression to the unqualified, non-atomic version of the named
    type. This construction is called a cast." (6.5.4p6)

    The word "cast" is italicized, an ISO convention indicating that the
    sentence in which it occurs is the official definition of that term.

    Based on surveying all you quoted, I basically nailed it above.

    Not quite. A cast is "this construction", namely "preceding an
    expression by a parenthesized type name". It describes a portion of the
    text of a program. What you described as a cast is the semantics
    associated with that construct when the program is compiled, not the
    construct itself. (float)3 is a cast. "Convert 3 to a float" is not.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sat Aug 10 11:03:02 2024
    On 09/08/2024 18:08, David Brown wrote:
    On 09/08/2024 02:56, Bart wrote:
    On 08/08/2024 23:32, David Brown wrote:
    On 08/08/2024 21:09, Bart wrote:

    Sorry but my function is perfectly valid. It's taking a Bool value
    and converting it to an int.

    No, it is not.

    Attempting to use the value of a non-static local variable that has
    not been initialised or assigned is undefined behaviour.  Your
    function is garbage.  No one can draw any conclusions about how
    meaningless code is compiled.

    FFS. You really think that makes a blind bit of difference?

    Yes, I do.

    A variable is not initialised, so the bool->int code shown must be
    just random rubbish generated by the compiler? You think I wouldn't
    spot if there was something amiss?

    You wrote C code that had something amiss!

    How often are you going to do this?  Write some piece of meaningless
    crap with undefined behaviour or - at best - no observable behaviour at
    all, compile with silly choices of flags, and then make nonsensical
    claims about what compilers do or how C is defined?

    What did I get wrong about that particular conversion?

    I write such fragments of code for my own compilers a hundred times a day.

    It only seems to cause a hoo-hah with your favourite compiler.

    As for UB, what exactly is undefined about:

       void DB(void) {
           _Bool b=false;
           int i;
           i=b;
       }

    There is a difference in the code.  One (BC) has no defined behaviour.
    The other (DB) has defined behaviour with no observable behaviour.

    Ah. No observable behaviour.

    This is indeed a problem with an optimising compiler, because it is in
    that mode that a compiler will detect that your code has no observable
    effects (other than in half a dozen ways I could mention, such as
    looking at the generated code!), and decides it's not going to bother
    producing it.

    This is why I recommend -O0 with such compilers. Or better, use a
    simpler compiler.

    (Mine for example have absolute no problem with meaningless fragments,
    so long as they satisfy language rules. They will even produce comment
    lines like these:

    ;------------------------------
    ...
    ;------------------------------

    to more easily isolate a function body from entry/exit code.

    For something available to everyone, then Tiny C is available on
    godbold.org.)


    not a surprise that a compiler generates no code for either of them.

    So, even initialised, it tells me nothing about what might be involved
    in bool->int conversion. It is useless.


    Agreed.  Nobody suggested your code above as a good idea.

    Now I'll try it with -O0 (line breaks added):

    BC:
             push    rbp
             mov     rbp, rsp

             movzx   eax, BYTE PTR [rbp-1]
             mov     DWORD PTR [rbp-8], eax

             nop
             pop     rbp
             ret
    DB:
             push    rbp
             mov     rbp, rsp
             mov     BYTE PTR [rbp-1], 0

             movzx   eax, BYTE PTR [rbp-1]
             mov     DWORD PTR [rbp-8], eax

             nop
             pop     rbp
             ret

    Exactly the same code, except DB has an extra line to initialise that
    value.

    Are you surprised it is the same? I am 99% sure that you already knew
    this, but were pretending that the code was meaningless, for reasons
    that escape me.


    Instead of trolling with what you know, without doubt, are pointless
    straw men, why not apply a little thought and write functions that make sense?

    When you are testing code fragments, they DON'T make sense. Hence -O0 to
    avoid having to soft-soap or cajole the compiler into producing relevant
    code.

    It's to make life easier not harder. The only thing with -O0 is to learn
    to disregard the irrelevant bits. But at least YOU get to say what is irrelevant.


    I'm giving up trying to help you - at least until you show some hint of trying to learn.

    What the fuck are you on about?

    I'm suggesting using -O0 because it is easier: you write code fragments
    without have to write a complete, meaningful program that has observable behaviour.

    Which is quite hard to do; benchmaking code from an optimising compiler
    can be challenging, since it is easy for it to circumvent the very task
    you're trying to measure. Apparently, program runtime does not count as 'observable behaviour' so it is not something that attempt is made to
    preserve.



      I will still make posts pointing out when you write
    nonsense that might confuse or mislead others, but I'll stop trying to explain things unless you specifically ask.

    So will I, to other people.

    The set of tests posted by MS, compiler with optimising options with
    Clang, shed no light on bool to int conversion.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sat Aug 10 11:17:39 2024
    On 09/08/2024 21:01, David Brown wrote:
    On 09/08/2024 20:54, Thiago Adams wrote:

    I don't know what you are referring to here.  But if you are using
    compiler explorer, I encourage you to look at the generated output for a
    wide range of targets, including 8-bit AVR, 16-bit MSP430, 32-bit ARM,
    and 64-bit x86.  Use gcc -O1 or -O2 in every case.

    When would you choose -O2 over -O1 or vice versa? Could a similar
    circumstance cause you to choose -O0? Why not -O3?

    In fact, why is there a -O0 option at all?

      (Ignore Bart's
    ignorant blatherings about optimisation.)

    [To TA:]

    Yes do. But don't complaint to me when your test code results in
    meaningless or misleading output, or no output at all.

    Actually, I would recommend looking at both (eg. -O0 and -O1) so that
    you can see if the compiler's optimiser has been over-zealous in
    eliminating code, or has chopped out key bits, so that you might modify
    your test code.

    I would recommend also looking at the Tiny C option on godbolt when
    comparing x86 code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Thiago Adams on Sat Aug 10 17:14:19 2024
    On 10/08/2024 14:15, Thiago Adams wrote:
    Em 8/10/2024 7:17 AM, Bart escreveu:
    On 09/08/2024 21:01, David Brown wrote:
    On 09/08/2024 20:54, Thiago Adams wrote:

    I don't know what you are referring to here.  But if you are using
    compiler explorer, I encourage you to look at the generated output
    for a wide range of targets, including 8-bit AVR, 16-bit MSP430,
    32-bit ARM, and 64-bit x86.  Use gcc -O1 or -O2 in every case.

    When would you choose -O2 over -O1 or vice versa? Could a similar
    circumstance cause you to choose -O0? Why not -O3?

    In fact, why is there a -O0 option at all?

       (Ignore Bart's
    ignorant blatherings about optimisation.)

    [To TA:]

    Yes do. But don't complaint to me when your test code results in
    meaningless or misleading output, or no output at all.

    Actually, I would recommend looking at both (eg. -O0 and -O1) so that
    you can see if the compiler's optimiser has been over-zealous in
    eliminating code, or has chopped out key bits, so that you might
    modify your test code.

    I would recommend also looking at the Tiny C option on godbolt when
    comparing x86 code.




    Bart, Does your compiler support the `bool` type, where the value is
    always either 1 or 0?

    There is a bool type, but it is treated like unsigned char, so is non-conforming.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Thiago Adams on Sun Aug 11 13:30:08 2024
    On 11/08/2024 13:23, Thiago Adams wrote:
    Em 8/10/2024 9:10 PM, Keith Thompson escreveu:
    Thiago Adams <[email protected]> writes:
    Em 8/10/2024 1:14 PM, Bart escreveu:

    Bart, Does your compiler support the `bool` type, where the value
    is always either 1 or 0?
    There is a bool type, but it is treated like unsigned char, so is
    non-conforming.

    I do the same in my compiler , when I transpile from C99 to C89.
    I was thinking how to make it conforming.
    For instance on each write.

    bool b = 123; -> unsigned char b = !!(123);

    The problem this does not fix unions, writing on int and reading from
    char.

    I don't think you need to fix that.

    [....]

    Summary:

    Conversion from any scalar type to _Bool is well defined, and must yield
    0 or 1.


    I will fix in terns of expressions types.

     - In this case cast to bool
     - Assignment to bool

    It's possible to force a representation other than 0 or 1 into a _Bool
    object, bypassing any value conversion.

    Conversion from _Bool to any scalar type is well defined if the
    operand is a _Bool object holding a representation of 0 or 1.

    Conversion from _Bool to any scalar type for an object holding some
    representation other than 0 or 1 either yields 0 or 1 (depending
    on the low-order bit) or has undefined behavior.

    I did a sample now..

    #include <stdio.h>

    int main() {
        union {
            int i;
            _Bool b;
        } data;
        data.i = 123;
        printf("%d", data.b);
    }

    it printed 123 not 1.
    So I think the assignment and cast covers all/most cases.
    (From some previous tests I thought this was printing 1)

    That's little different from this example:

    #include <stdio.h>

    int main() {
    union {
    int i;
    float b;
    } data;
    data.i = 123;
    printf("%e", data.b);
    }

    I get some arbitrary float value printed. You're supposed to know what
    you are doing with unions.

    It's not something I'd worry about. If you're trying to make a safer C,
    then you'd have to ban unions, or ban bools inside unions that could be
    read out as a different type, or introduce tagged unions so that runtime checking can be done.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to James Kuyper on Sun Aug 11 17:15:09 2024
    James Kuyper <[email protected]> writes:

    Thiago Adams <[email protected]> writes:
    ...

    I also curious about how bool works.

    Values converted to bool became 0 or 1.
    When this conversion happens, at read or write? Both?

    You can take a value obtained by reading an object, or a value produced
    by evaluating an expression, and convert that value to a different type.
    That value can later be stored in an object, or it could be used as one
    of the operands for an expression. The conversion isn't associated with either the read or the write. Many conversions occur implicitly, a cast
    is used to explicitly make a conversion occur.

    int x = 3;
    bool b = x;

    In the above code, an implicit conversion from int to bool occurs after reading the value of 3 from x, and occurs before writing to bool.

    b = !(bool)(x-3);

    In this code, the conversion occurs after the value of 3 is retrieved
    from x, and after 3 is subtracted from it. That result of 0 is then converted to bool, and then the ! operator is applied to it. Finally,
    the result is written to b. So you see, it doesn't make sense to connect
    the conversion with either the read or the write.

    The assignment statement

    b = !(bool)(x-3);

    performs two conversions. The (bool) cast converts an int value
    to a bool value. The assignment to b converts the value of the !
    subexpression (which is of type int) to bool before assigning it.

    Assignment operators always convert the value being assigned to
    the type of the assignment expression, even if the two types
    are the same.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun Aug 11 17:07:56 2024
    Keith Thompson <[email protected]> writes:

    David Brown <[email protected]> writes:

    On 09/08/2024 01:14, Keith Thompson wrote:

    David Brown <[email protected]> writes:
    [...]

    A _Bool is always either 0 or 1. The conversion is whatever the
    compiler needs to give an int of value 0 or 1.

    The value of a _Bool object is always either 0 or 1 *unless* the
    program does something weird.

    True. But attempting to use a _Bool object (as a _Bool) that does not
    contain either 0 or 1 is going to be undefined behaviour (at least it
    was on the platform where I saw this happen as a code bug).

    It depends on whether representations with non-zero padding bits are
    treated as trap representations (non-value representations in C23) or
    not.

    In C99 and C11, iirc, the width of _Bool may be any value between
    1 and CHAR_BIT. If the width of _Bool is greater than 1, a _Bool
    may have a well-defined value that is neither 0 or 1. My guess is
    most implementations define the width of _Bool as 1, but they don't
    have to (again, iirc, in C99 and C11).

    It doesn't specify whether setting the padding bits to 1 results in a
    non-value representation.

    That's probably an implementation-defined issue, is it not?

    I'm not sure whether it's implementation-defined or unspecified.
    I don't see any mention of trap/non-value representations in Annex J.

    [...]

    6.2.6.1 p 2;

    J.3.13 p 1, third subpoint.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Kaz Kylheku on Sun Aug 11 17:32:54 2024
    Kaz Kylheku <[email protected]> writes:

    On 2024-08-09, Keith Thompson <[email protected]> wrote:

    Kaz Kylheku <[email protected]> writes:
    [...]

    It almost makes sense to speak of "implicit cast" (i.e. coercion) in C,
    because of what happens implicitly being so unsafe.

    I disagree, because that's not what "cast" means.

    "cast" means to (try to) project a value into another type.

    In programming, "cast" means to force a value to be expressed
    in a particular type, regardless of whether the output type
    differs from the input type.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Kaz Kylheku on Sun Aug 11 17:27:29 2024
    Kaz Kylheku <[email protected]> writes:

    In computer science, we refer to unsafe conversion as coercion.

    In most programming languages, "coercion" refers to any implicit
    conversion (perhaps requiring a change in type), regardless of
    whether the conversion is safe or is not.

    THe cast notation is C's coercion operation.

    Absolutely not. "Coercion" usually means an implicitly caused
    conversion, following the term being introduced in Algol 68.
    A cast in C is exactly the opposite, always explicit.

    Disclaimer: the above comments based on cursory research to
    reinforce my memory of literature read years ago, and supported
    by Wikipedia.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Thiago Adams on Sun Aug 11 17:43:32 2024
    Thiago Adams <[email protected]> writes:

    I also would like to understand better why integer promotions were created.

    My guess..it is because the values are used in registers and there is
    no "char" size register so the values are converted in a bigger type.
    Is my guess correct?

    The motivations for integer promotion rules are purely historical.
    You're looking for answers in the wrong places.

    Also, it would be better for your understanding of C if you would
    stop thinking about what is going on at the level of actual
    hardware. Doing that serves to confuse a lot more than it helps.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun Aug 11 17:46:58 2024
    Keith Thompson <[email protected]> writes:

    Bart <[email protected]> writes:
    [...]

    Take:

    int a; double x;

    x = (double)a;

    The cast is implicit here but I've written it out to make it clear.

    [...]

    The *conversion* could be done implicitly, but you've used a cast (i.e.,
    an explicit conversion) to make it clear.

    The statement assigning to x performs two conversions: an explicit
    one caused by the cast, and an implicit one caused by the assignment
    operation.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Tim Rentsch on Mon Aug 12 02:00:15 2024
    On 12/08/2024 01:46, Tim Rentsch wrote:
    Keith Thompson <[email protected]> writes:

    Bart <[email protected]> writes:
    [...]

    Take:

    int a; double x;

    x = (double)a;

    The cast is implicit here but I've written it out to make it clear.

    [...]

    The *conversion* could be done implicitly, but you've used a cast (i.e.,
    an explicit conversion) to make it clear.

    The statement assigning to x performs two conversions: an explicit
    one caused by the cast, and an implicit one caused by the assignment operation.

    The 'x' term is the other side of the cast from the 'a' term.

    So after '(double)a' has been evaluated, both sides of '=' have the type 'double', so no further conversion is needed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Bart on Sun Aug 11 20:37:08 2024
    Bart <[email protected]> writes:

    On 12/08/2024 01:46, Tim Rentsch wrote:

    Keith Thompson <[email protected]> writes:

    Bart <[email protected]> writes:
    [...]

    Take:

    int a; double x;

    x = (double)a;

    The cast is implicit here but I've written it out to make it clear.

    [...]

    The *conversion* could be done implicitly, but you've used a cast (i.e., >>> an explicit conversion) to make it clear.

    The statement assigning to x performs two conversions: an explicit
    one caused by the cast, and an implicit one caused by the assignment
    operation.

    The 'x' term is the other side of the cast from the 'a' term.

    So after '(double)a' has been evaluated, both sides of '=' have the
    type 'double', so no further conversion is needed.

    The C standard requires that a conversion take place as part of
    the assignment, even when the types are the same.

    Furthermore, there are cases where having to do a conversion from
    one type to the same type has semantic consequences, even though
    the types are the same.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Bart on Mon Aug 12 00:33:38 2024
    Bart <[email protected]> writes:

    On 09/08/2024 00:17, Keith Thompson wrote:

    Bart <[email protected]> writes:
    [...]

    Take:

    int a; double x;

    x = (double)a;

    The cast is implicit here but I've written it out to make it
    clear.

    [...]

    Since C likes to use the term 'cast' for such conversions, [...]

    The C standard uses the term 'cast' only for explicit conversions,
    and never uses the term 'cast' for implicit conversions. You
    should do the same.

    It just seems to irk the pedantics here.

    What bothers people is not you using the wrong terminology. What
    bothers people is you being a self-centered jerk, and deliberately
    using incorrect terminology just to annoy people.

    Incidentally, the word "pedantic" is an adjective. The noun form
    is "pedant". People who complain about you using terminology
    incorrectly are not being pedants. They simply are offended by
    your never-ending efforts to be a pest and an asshole.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Vir Campestris@21:1/5 to Tim Rentsch on Mon Aug 12 11:51:34 2024
    On 12/08/2024 01:43, Tim Rentsch wrote:
    Thiago Adams <[email protected]> writes:

    I also would like to understand better why integer promotions were created. >>
    My guess..it is because the values are used in registers and there is
    no "char" size register so the values are converted in a bigger type.
    Is my guess correct?

    The motivations for integer promotion rules are purely historical.
    You're looking for answers in the wrong places.

    Also, it would be better for your understanding of C if you would
    stop thinking about what is going on at the level of actual
    hardware. Doing that serves to confuse a lot more than it helps.

    I think I feel my ears burning!

    Thiago, an example - if I have two integers, and I want to multiply
    them, the output may be too big to fit in an integer. Cast them both to
    a size twice as big, multiply them, and you know the output will fit.
    You can then check it will fit back into an int.

    Andy
    --

    Being careful of course because there's no guarantee than long is bigger
    than int...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Mon Aug 12 12:24:55 2024
    On 11/08/2024 21:38, Keith Thompson wrote:
    Thiago Adams <[email protected]> writes:

    - In this case cast to bool
    - Assignment to bool

    You need to cover all cases where a scalar value is converted to _Bool.
    That includes (explicit) casts,

    Is there any other kind?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Tim Rentsch on Mon Aug 12 12:21:21 2024
    On 12/08/2024 08:33, Tim Rentsch wrote:
    Bart <[email protected]> writes:

    On 09/08/2024 00:17, Keith Thompson wrote:

    Bart <[email protected]> writes:
    [...]

    Take:

    int a; double x;

    x = (double)a;

    The cast is implicit here but I've written it out to make it
    clear.

    [...]

    Since C likes to use the term 'cast' for such conversions, [...]

    The C standard uses the term 'cast' only for explicit conversions,
    and never uses the term 'cast' for implicit conversions. You
    should do the same.

    I'm not the C standard.


    It just seems to irk the pedantics here.

    What bothers people is not you using the wrong terminology. What
    bothers people is you being a self-centered jerk, and deliberately
    using incorrect terminology just to annoy people.

    Incidentally, the word "pedantic" is an adjective. The noun form
    is "pedant". People who complain about you using terminology
    incorrectly are not being pedants.

    My bad.


    They simply are offended by
    your never-ending efforts to be a pest and an asshole.

    Interesting, as people could say that about you as well.

    Some people here like to drag every discussion, every topic, into the
    realm of the C Standard. And a few like to seize on the slightest misuse
    of a term which has a specific meaning in C standard, even though that
    has no practical bearing on the discussion

    But what about terms that do not appear in the C Standard?

    Since it doesn't use 'implicit cast', presumably I can give that any
    meaning I like, including meaning 'implicit type conversion'. 'cast' is
    shorter to write than 'conversion' or 'coercion', and adds variety to sentences.

    I notice that no one picked on the casual use of terms I found in the
    N1570 standard, or just waved them aside:

    * Using 'explicit cast' (which implies 'implicit cast') (6.5.4p3)

    * Using 'type casts' (redundant 'type') (H.2.4P1)

    * Using ' ... conversions (casts) ...' where, if 'conversions' can
    include implicit ones, would imply implicit casts too (H.2.4p4/5)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Mon Aug 12 15:36:34 2024
    On 09/08/2024 18:12, David Brown wrote:
    On 09/08/2024 12:04, Bart wrote:
    On 09/08/2024 00:17, Keith Thompson wrote:
    Bart <[email protected]> writes:
    [...]
    Take:

         int a; double x;

         x = (double)a;

    The cast is implicit here but I've written it out to make it clear.

    [...]

    The *conversion* could be done implicitly, but you've used a cast (i.e., >>> an explicit conversion) to make it clear.

    There is no such thing as an "implicit cast" in C.


    Suppose I write this code:

         x = a;                  // implicit 'conversion' >>      x = (double)a;          // explicit 'conversion'


    My compiler produces these two bits of AST for the RHS of both
    expressions:

    1 00009 r64---|---2 convert: sfloat_c i32 => r64
    1 00009 i32---|---|---1 name: t.main.a.1

    1 00010 r64---|---2 convert: sfloat_c i32 => r64
    1 00010 i32---|---|---1 name: t.main.a.1

    So whatever you call that `(double)` part of the second line, which is
    written explicitly, exactly the same thing is done internally (ie
    'implicitly') to the first line. (The 09/10 are line numbers.)


    You've written it yourself.  Both are conversions - one is implicit, the other is explicit.

    Something you might observe here is that both lines are represented,
    despite the fact that the x's value is immediately overwritten after the
    first assignment, and its value is not used even after the second.

    What follows was actually not shown, but it doesn't matter.

    You presumably would use optimisation flags every time, so risking unpredictable chunks of the output disappearing depending on how early
    they cut in.

    In fact, if I do this (a, x have int, double types):

    a;
    x;

    my compiler produces some output, namely, evaluating and leaving results
    into a register:

    mov eax, [rbp+`main.a]
    movq XMM4, [rbp+`main.x]

    gcc however produces no output even with -O0.

    (I'd wanted to see what gcc did with an 'open' expression, one which
    would not be coerced into a particularly type, but those are rare in C
    code.)



    Since C likes to use the term 'cast' for such conversions, I don't see
    a problem with talking about implicit and explicit versions.


    C does not "like" to use the term "cast" for anything other than cast operations, as defined by the C standards.  Implicit conversions are not casts.

    /You/ might like to call implicit conversions "casts", but you'd be
    wrong to do so.

    It just seems to irk the pedantics here.

    You mean, people who know what they are talking about rather than those
    that make up stuff as they go along?


    You yourself said that my own language is just C with a different
    syntax. Since I've worked on various implementations of it most of my
    life, is it possible that I might know what I'm talking about as well?

    What I haven't done with my language is to create a reference document
    with its own precisely defined nomenclature, whose existence then
    requires that any casual discussion about ANY aspects of it to use
    exactly those terms and no others.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ben Bacarisse@21:1/5 to Keith Thompson on Mon Aug 12 16:57:24 2024
    Keith Thompson <[email protected]> writes:

    Tim Rentsch <[email protected]> writes:
    [...]
    Furthermore, there are cases where having to do a conversion from
    one type to the same type has semantic consequences, even though
    the types are the same.

    What are these cases?

    The case that comes to mind is where the result of a floating point
    expression is represented with more precision than the type can hold
    when stored.

    6.3.1.8 p2:
    The values of floating operands and of the results of floating
    expressions may be represented in greater range and precision than
    that required by the type; the types are not changed thereby.[63]

    The footnote explains that the implicit conversion done on assignment
    will change the value even though the types may be the same:

    [63] The cast and assignment operators are still required to remove
    extra range and precision.

    --
    Ben.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Ben Bacarisse on Mon Aug 12 10:04:17 2024
    Ben Bacarisse <[email protected]> writes:

    Keith Thompson <[email protected]> writes:

    Tim Rentsch <[email protected]> writes:
    [...]

    Furthermore, there are cases where having to do a conversion from
    one type to the same type has semantic consequences, even though
    the types are the same.

    What are these cases?

    The case that comes to mind is where the result of a floating point expression is represented with more precision than the type can hold
    when stored.

    Right.

    6.3.1.8 p2:
    The values of floating operands and of the results of floating
    expressions may be represented in greater range and precision than
    that required by the type; the types are not changed thereby.[63]

    The footnote explains that the implicit conversion done on assignment
    will change the value even though the types may be the same:

    [63] The cast and assignment operators are still required to remove
    extra range and precision.

    A related item is that the rule for 'return' is not the same as
    assignment. When returning a value from a function, the value
    of the return expression is converted to the return type of the
    function, /unless/ the type of the return expression is the same
    as the return type of the function, in which case no conversion
    takes place (and so extra range and precision may be retained).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to James Kuyper on Tue Aug 13 11:18:31 2024
    On 10/08/2024 00:29, James Kuyper wrote:
    Bart <[email protected]> writes:

    Also, what is exactly is the difference between 'explicit conversion'
    and 'explicit cast'?

    None


    It is possible that the C standards authors here were envisaging other
    types of explicit conversion in the future. For example, C++ has
    multiple forms of explicit conversion, at least some of which could
    reasonably be copied into C (such as the functional notation explicit
    type conversions - "int(x)", meaning the same as the cast "(int) x").

    You could argue that the C standards use the term "explicit conversion"
    to make it clear that there are no /implicit/ conversions involved. It
    does not matter if the explicit conversion is done with a cast operator,
    or in some other way - even if C (currently) has no other way to achieve
    the effect.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Tue Aug 13 11:23:18 2024
    On 10/08/2024 02:11, Bart wrote:
    On 10/08/2024 00:58, Keith Thompson wrote:

    In any case, while there may be some ambiguity about whether all casts
    specify conversions, it is unambiguous that an implicit conversion is
    not a cast.


    Here are some comments from the Tiny C source code:

            /* XXX: implicit cast ? */
            /* compute bigger type and do implicit casts */

    This is from some gcc code:

         ..and some compilers cast it to int implicitly ...

    Come on, everybody's at it. Unless we're trying do to a reference
    document, then /it really doesn't matter/.



    Do you also throw shopping trolleys into rivers, just because you see
    that other people have done so?

    comp.lang.c posters, compiler writers, and even the C standards writers,
    are fallible humans - they make mistakes sometimes. We all do. Why not
    try to correct the mistakes, or learn from them, rather than duplicating
    and spreading them?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to James Kuyper on Tue Aug 13 11:34:33 2024
    On 09/08/2024 23:29, James Kuyper wrote:
    Bart <[email protected]> writes:


    Why can't there also be a similar correlation between 'implicit
    conversion' and 'implicit cast'?

    The C standard defines "implicit conversion" and "explicit conversion"
    in 6.3p1, and the definition it provides for "explicit conversion" is
    "those [conversions] that result from a cast operation". it provides a grammar production for a cast expression, and none for a implicit cast expression.

    What would the syntax look like for an implicit cast?

    There isn't any. If there was, it wouldn't be implicit!


    even if it did, surely people ought to be allowed to use alternate
    terms for an informal discussion? This is a not a committee meeting.

    Every time you use a term with a standard-define meaning in a way that doesn't match the meaning defined for it by the standard, you create potential confusion. If that's what you want to do, go ahead, but it
    seems an odd thing to do.


    What standard-defined meaning is attributed to 'implicit cast'? Since as everyone keeps telling me, that term is not used.

    Does that mean it's up for grabs?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to James Kuyper on Tue Aug 13 07:51:37 2024
    On 8/9/24 18:29, James Kuyper wrote:
    Bart <[email protected]> writes:
    ...
    Why can't there also be a similar correlation between 'implicit
    conversion' and 'implicit cast'?

    The C standard defines "implicit conversion" and "explicit conversion"
    in 6.3p1, and the definition it provides for "explicit conversion" is
    "those [conversions] that result from a cast operation". it provides a grammar production for a cast expression, and none for a implicit cast expression.

    Note, in particular, that if there were such a thing as an implicit
    cast, then, as the name implies, an implicit cast would qualify as a
    cast. Since, according to the above definition, all conversions that
    result from casts would qualify as "explicit conversions", that would
    include conversions that result from implicit casts. In other words, all conversions would be explicit conversions. In addition, per 6.3p1, the conversions described in 6.3 would also be implicit conversions, which
    is just plain ridiculous.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to James Kuyper on Tue Aug 13 14:01:13 2024
    On 13/08/2024 12:51, James Kuyper wrote:
    On 8/9/24 18:29, James Kuyper wrote:
    Bart <[email protected]> writes:
    ...
    Why can't there also be a similar correlation between 'implicit
    conversion' and 'implicit cast'?

    The C standard defines "implicit conversion" and "explicit conversion"
    in 6.3p1, and the definition it provides for "explicit conversion" is
    "those [conversions] that result from a cast operation". it provides a
    grammar production for a cast expression, and none for a implicit cast
    expression.

    Note, in particular, that if there were such a thing as an implicit
    cast, then, as the name implies, an implicit cast would qualify as a
    cast. Since, according to the above definition, all conversions that
    result from casts would qualify as "explicit conversions", that would
    include conversions that result from implicit casts. In other words, all conversions would be explicit conversions. In addition, per 6.3p1, the conversions described in 6.3 would also be implicit conversions, which
    is just plain ridiculous.

    If that were the case then the use of 'cast' would be more likely to be qualified.

    I get that the standard mainly intends 'cast' to mean that bit of
    syntax, '(T)X', by which you can force a particular conversion.

    But '(T)X' can also be described as a conversion, or an explicit
    conversion, or a coercion (this more where an automatic implicit
    conversion does not exist), as well as a cast.

    Then I think it's perfectly acceptable to use all these terms
    interchangeably when there is no confusion. In that case, using
    'implicit cast' to mean the same as explicit or automatic conversion
    shouldn't present any problems.

    Other than to C Standard purists.

    Of course, if it is necessary to be much more precise and specific /for
    a reason/, then you'd need to take more care.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Tue Aug 13 21:51:04 2024
    On 13/08/2024 20:46, Keith Thompson wrote:
    Bart <[email protected]> writes:
    [...]
    I get that the standard mainly intends 'cast' to mean that bit of
    syntax, '(T)X', by which you can force a particular conversion.

    No, the standard *exclusively* uses "cast" to mean that bit of syntax
    (except in Annex H, but those incorrect uses of the word are corrected
    in C23). But of course you know that.

    Blimey, you're not to let this go are you?

    N1570 contains 2 instances of 'casting' (one is to do with type
    conversion; the other with voting).

    Plus 9 instances of 'casts'. And 56 instances of 'cast', but many of
    those are part of 'cast operator', or 'cast expression' (separate from /cast-expression/).

    There is also the confusing '(cast)', with 'cast' in italics, but that's
    in the already discredited Annex H. (Confusing because I thought a cast included the parentheses.)

    You're making me as obsessed with this as you are!

    Well, here's an interesting titbit: in my syntax, 'cast' is a reserved
    word that can be used for type conversion as 'cast(X, T)', meaning
    '(T)X' in C, used when the normal 'T(X)' is ambiguous.

    So, for all this fuss about 'cast' in the Standard, it isn't even a
    reserved word in C itself.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Bart on Tue Aug 13 16:46:15 2024
    Bart <[email protected]> writes:

    So, for all this fuss about 'cast' in the Standard, it isn't even
    a reserved word in C itself.

    Many or most of the defined terms in the C standard are not
    keywords or reserved words in the C language.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Tim Rentsch on Wed Aug 14 00:56:01 2024
    On 14/08/2024 00:46, Tim Rentsch wrote:
    Bart <[email protected]> writes:

    So, for all this fuss about 'cast' in the Standard, it isn't even
    a reserved word in C itself.

    Many or most of the defined terms in the C standard are not
    keywords or reserved words in the C language.

    My point was, if 'cast' /was/ a reserved word, then you'd have to get it
    just right with no quibbling.

    But it isn't. A bit silly to apply just as strict rules to humans
    discussing topics in English. It's not as though usenet messages are
    going to be compiled with 'gcc -Wpedantic'!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Bart on Sun Aug 18 03:37:27 2024
    Bart <[email protected]> writes:

    On 14/08/2024 00:46, Tim Rentsch wrote:

    Bart <[email protected]> writes:

    So, for all this fuss about 'cast' in the Standard, it isn't
    even a reserved word in C itself.

    Many or most of the defined terms in the C standard are not
    keywords or reserved words in the C language.

    My point was, if 'cast' /was/ a reserved word, then you'd have to
    get it just right with no quibbling.

    I knew what you meant.

    But it isn't. A bit silly to apply just as strict rules to humans
    discussing topics in English.

    Not silly at all, in the context of educated discussions in a
    technical newsgroup.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Tue Sep 3 06:02:20 2024
    Keith Thompson <[email protected]> writes:

    Tim Rentsch <[email protected]> writes:

    Keith Thompson <[email protected]> writes:

    David Brown <[email protected]> writes:

    On 09/08/2024 01:14, Keith Thompson wrote:

    David Brown <[email protected]> writes:
    [...]

    A _Bool is always either 0 or 1. The conversion is whatever
    the compiler needs to give an int of value 0 or 1.

    The value of a _Bool object is always either 0 or 1 *unless* the
    program does something weird.

    True. But attempting to use a _Bool object (as a _Bool) that
    does not contain either 0 or 1 is going to be undefined behaviour
    (at least it was on the platform where I saw this happen as a
    code bug).

    It depends on whether representations with non-zero padding bits
    are treated as trap representations (non-value representations in
    C23) or not.

    In C99 and C11, iirc, the width of _Bool may be any value between 1
    and CHAR_BIT. If the width of _Bool is greater than 1, a _Bool may
    have a well-defined value that is neither 0 or 1. My guess is most
    implementations define the width of _Bool as 1, but they don't have
    to (again, iirc, in C99 and C11).

    C11 (N1570) isn't 100% clear, but I think you're right. The
    conversion rank of _Bool is less than the rank of the char types.
    I don't see an explicit statement that this implies that _Bool has
    less precision than unsigned char, [...]

    C11 (and I think also C99, but I haven't checked that) states
    requirements that guarantee _Bool has a width no larger than the
    width of unsigned char. I'm sure you can locate the relevant
    passages.


    It doesn't specify whether setting the padding bits to 1 results
    in a non-value representation.

    That's probably an implementation-defined issue, is it not?

    I'm not sure whether it's implementation-defined or unspecified.
    I don't see any mention of trap/non-value representations in Annex
    J.

    [...]

    6.2.6.1 p 2;

    So it's implementation-defined. [...]

    Quoting the standard so that everyone else doesn't have to go look
    it up (and guess which edition you're referring to). You might
    consider doing that yourself.

    I choose the contents of my comments to be what I think is
    appropropriate under each individual set of circumstances.
    Please keep your patronizing preaching to yourself.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)