Forum > General

C not and ternary conversion to pascal question.

<< < (2/6) > >>

Khrys:
This is the same thing:


--- Code: C  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---A_Double = Condition ? Some_Double_Variable : 0;
C++ operator precedence makes sense for once (unlike Pascal). There are 3 operators used here: assignment (=), logical negation (!) and ternary (?:). Negation has the highest precedence, so the condition is inverted. The ternary and assignment operators have the same precedence, but are right-to-left associative, so the ternary comes next. The assignment comes last.

The condition is implicitly converted to bool in any case, so I think this is just an oversight by the author; it's perfectly possible to invert the outcome just by swapping the ternary operands.

Thaddy:
Yes it is nasty in more than one way, because a C++ Boolean <> a C Boolean.
In C++ Boolean is a dedicated type true/false, so !false == true and ONLY true.
(! is not, negation)
whereas in C it depends, but in modern C it is usually 0 or 1 and in older C it is 0 or any other value than 0.
C knows _Bool and from C99 bool which also resolve to 0 and 1.
Usually through stdbool.h.
Since Pascal also defines them as 0 and 1 one would expect interoperability, but that is not always the case because of optimization issues. We discussed that fairly recently. E.g. the clang compiler optimizes bool true into values other than 1 even if it is defined as 1 in the language. This can lead to problems when interfacing C code and Pascal code.
The mentioned discussion contains some C code by me that demonstrates the issue: in -O1 it is 0 and 1, as expected, but in -O4 it is 0 and any other value the compiler feels like using. There was even a deranged proposal to "fix" this at the fpc side, meaning changing the Pascal language definition of Boolean.
The best way to solve the issue is indeed like in the C++ code above: the negation is probably to avoid the 0/1 optimization paradox and you can use the same in Pascal. Basically testing for true is testing not 0 and so in the case of the ternary operator we do not test for 1. This looks like black magic at first, but it works to avoid the optimization issue. It took me quite a while to realize what was happening and why.

--- Quote ---The condition is implicitly converted to bool in any case, so I think this is just an oversight by the author; it's perfectly possible to invert the outcome just by swapping the ternary operands.
--- End quote ---
It is intentional and not an oversight at all: the programmer of that C++ code was obviously aware of the issue.
Nasty: the only thing you can be sure of is false!


--- Quote from: jamie on September 24, 2024, 02:52:50 am ---Its first negating the operation and then follows the actual selection.

I don't know why it just didn't swap the selections around!

or maybe I still have it wrong! :(

--- End quote ---
I hope it now makes sense to you too.


--- Quote from: Khrys on September 24, 2024, 08:56:16 am ---This is the same thing:

--- End quote ---
No it isn't, because any other value than all $F's would negate to another value <> 0 (keeps being true), so if you want to really test for true and in all cases, you have to test for not false (0) and that is exactly why that C++ code looks like it does.

This also resolves the previous discussion.
The only way to reliably test for true in a compiler and optimization independent way is to test for not false.
(The hell with language definitions and just testing for bit 0 being set or not. If you are a compiler writer, simply ignore that!)

Warfley:
One option is that not every condition is a bool, and the easiest way to convert something into a bool is the not operator. For example:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---bool isBool(bool b) {    return true;}template <typename t>bool isBool(t b) {    return false;} int main() {    std::cout << "2 is " << (2 ? "true" : "false") << " in ternary if\n";    std::cout << "is 2 a bool? " << isBool(2) << "\n";        std::cout << "!!2 is " << (!!2 ? "true" : "false") << " in ternary if\n";    std::cout << "is !!2 a bool? " << isBool(!!2) << "\n";     return 0;}
Result:

--- Quote ---2 is true in ternary if
is 2 a bool? 0
!!2 is true in ternary if
is !!2 a bool? 1
--- End quote ---
As you can see, an integer can be used as a bool in if statements, but it is not of type bool. So if you have a function from a C library, which returns an int (as C pre 99 did not have a bool type), but you want it to be interpreted as a bool, you can simply write !! to do that.
Ususally when you see "random" negations it's to convert a non bool expression to bool.

Another possible cause is operator overloading. Maybe the ! operator is specifically overloaded for the type that is used for the comparison, or alternatively the bool operator is overloaded. So adding the ! will cause the operator to be triggered and thereby extra code will be executed.

In any case, without knowledge what the type and style of the condition is, we can't tell

Thaddy:
You fell into the same trap as I initially did. Negating a non-zero value does not negate to a zero value except for two cases, the case where the underlying type of any size is filled with $F, which in turn renders -1 on signed types, not one. And when exactly 0 or 1.
There is one thing you CAN be sure of and that is that the representation of false is always zero.
Hence to solve the equation without knowing the underlying type in all cases is simply negating false to obtain true.
How it negates is then not really important.
Bool implementations that rely on bit zero still satisfy true if set and all other implementations work too.
Sticking with true = not false is a reliable approach, and it keeps things simple and consistent. It’s always good to have a clear and predictable way to handle boolean values, especially when dealing with different languages and compilers.
It is actually a neat trick and it always works irrespective of compiler or optimization settings.

Things are not always what you expect. The counter proof to your example is my clang C code in the previous discussion.
Do not focus on how true is expressed, but how the negation of false looks like. That is the message.

Now I must admit this wasn't known nor obvious to me in over 45 years of programming. You can't rely on true other than it is not false and you can not make assumptions about true other that it is not false.

Summary:
False is consistently represented as 0 across most programming languages, true can be represented by any non-zero value for historical, practical, and design reasons.
Compilers often use bitwise negation where they should use logical negation. The latter renders 0 for any non-zero negation. Bitwise negation is only valid for exact 0/1 to equal logical negation.

tetrastes:

--- Quote from: Thaddy on September 24, 2024, 12:53:55 pm ---You fell into the same trap as I initially did. Negating a non-zero value does not negate to a zero value except for one case, the case where the underlying type of any size is filled with $F, which in turn renders -1 on signed types, not one.

--- End quote ---

C/C++ operator ! is logical negation, not bitwise, so !(any_nonzero_value) = 0. Take compiler and try, before writing.

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version