Recent

Author Topic: C not and ternary conversion to pascal question.  (Read 1633 times)

jamie

  • Hero Member
  • *****
  • Posts: 6735
C not and ternary conversion to pascal question.
« on: September 24, 2024, 02:47:39 am »
I have this in a C++ app which kind of throwing me off a little.

Code: Pascal  [Select][+][-]
  1. A_Double = ! (The Condition Test) ? 0:Some_Double_Variable;
  2.  
  3.  

Normally I would say that the "!" operator is generating a True/1 , False/0 value. But that wouldn't make any sense because
why bother with the "Some_Double_Variable" where You can simply specify it directly.

So basically the ! reverses the results but to generate a True/False  or 1/0 results.

But I don't think this is what's happening here.

Can someone shead some light on it?

is it simply reversing the results of the ternary or is it generating a 1 or 0 results?


The only true wisdom is knowing you know nothing

jamie

  • Hero Member
  • *****
  • Posts: 6735
Re: C not and ternary conversion to pascal question.
« Reply #1 on: September 24, 2024, 02:52:50 am »
I think i figured it out.

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! :(
The only true wisdom is knowing you know nothing

TRon

  • Hero Member
  • *****
  • Posts: 3650
Re: C not and ternary conversion to pascal question.
« Reply #2 on: September 24, 2024, 04:17:29 am »
You mean like this?
Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. const
  4.   Some_Double_Variable = 1.23456;
  5.   Condition_Test_Value = true;
  6.  
  7. function CIFF(condition: boolean; trueval, falseval: double): double;
  8. begin
  9.   if condition
  10.     then CIFF := trueval else CIFF := falseval;
  11. end;
  12.  
  13. function The_Condition_Test: boolean;
  14. begin
  15.   The_Condition_Test := Condition_Test_Value;
  16. end;
  17.  
  18. procedure tern1;
  19. var
  20.   A_Double: double;
  21. begin
  22.   if not The_Condition_Test
  23.     then A_Double := 0 else A_Double := Some_Double_Variable;
  24.   writeln('A_Double = ', A_Double:2:6);
  25. end;
  26.  
  27. procedure tern2;
  28. var
  29.   A_Double: double;
  30. begin
  31.   A_Double := CIFF(not The_Condition_Test, 0, Some_Double_Variable);
  32.   writeln('A_Double = ', A_Double:2:6);
  33. end;
  34.  
  35. begin
  36.   tern1;
  37.   tern2;
  38. end.
  39.  
This tagline is powered by AI (AI advertisement: Free Pascal the only programming language that matters)

Zvoni

  • Hero Member
  • *****
  • Posts: 2754
Re: C not and ternary conversion to pascal question.
« Reply #3 on: September 24, 2024, 07:40:07 am »
If not condition then adouble:=0 else adouble:=somedouble;
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: C not and ternary conversion to pascal question.
« Reply #4 on: September 24, 2024, 08:30:08 am »
Its first negating the operation and then follows the actual selection.

That's nasty, because some notations require a boolean predicate/test (i.e. a comparison etc.) to be in parentheses.

I think you've got it right, but I'd look very carefully at those values /in/ /case/ they are in any way demonic (I don't think "magical" is appropriate here) and there's something in there to catch the unwary... or that relies on a precedence quirk in the originally-used compiler.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Khrys

  • Full Member
  • ***
  • Posts: 108
Re: C not and ternary conversion to pascal question.
« Reply #5 on: September 24, 2024, 08:56:16 am »
This is the same thing:

Code: C  [Select][+][-]
  1. 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

  • Hero Member
  • *****
  • Posts: 16199
  • Censorship about opinions does not belong here.
Re: C not and ternary conversion to pascal question.
« Reply #6 on: September 24, 2024, 09:00:16 am »
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.
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!

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! :(
I hope it now makes sense to you too.

This is the same thing:
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!)
« Last Edit: September 24, 2024, 10:52:21 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

Warfley

  • Hero Member
  • *****
  • Posts: 1763
Re: C not and ternary conversion to pascal question.
« Reply #7 on: September 24, 2024, 12:25:06 pm »
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  [Select][+][-]
  1. bool isBool(bool b) {
  2.     return true;
  3. }
  4. template <typename t>
  5. bool isBool(t b) {
  6.     return false;
  7. }
  8.  
  9. int main() {
  10.     std::cout << "2 is " << (2 ? "true" : "false") << " in ternary if\n";
  11.     std::cout << "is 2 a bool? " << isBool(2) << "\n";
  12.    
  13.     std::cout << "!!2 is " << (!!2 ? "true" : "false") << " in ternary if\n";
  14.     std::cout << "is !!2 a bool? " << isBool(!!2) << "\n";
  15.  
  16.     return 0;
  17. }

Result:
Quote
2 is true in ternary if
is 2 a bool? 0
!!2 is true in ternary if
is !!2 a bool? 1
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
« Last Edit: September 24, 2024, 12:51:45 pm by Warfley »

Thaddy

  • Hero Member
  • *****
  • Posts: 16199
  • Censorship about opinions does not belong here.
Re: C not and ternary conversion to pascal question.
« Reply #8 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 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.
« Last Edit: September 24, 2024, 01:43:58 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

tetrastes

  • Hero Member
  • *****
  • Posts: 600
Re: C not and ternary conversion to pascal question.
« Reply #9 on: September 24, 2024, 01:37:12 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.

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

Khrys

  • Full Member
  • ***
  • Posts: 108
Re: C not and ternary conversion to pascal question.
« Reply #10 on: September 24, 2024, 01:39:44 pm »
This is the same thing:
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.

Apart from the possibility of operator overloads (as Warfley mentioned), yes, it is the same thing. You are so preoccupied with your realization that only testing for  false / 0  is reliable in general that you don't see how in this specific case (as a ternary expression) it is perfectly fine. The language standard says so, Warfley's test here says so, go look at the generated code if you still aren't convinced. There is no  cmp edi, 1  or any other kind of comparison with 1 in the output.

But why am I even arguing? Your entire premise here is wrong:
any other value than all $F's would negate to another value <> 0 (keeps being true)
C/C++ has separate bitwise and boolean operators,  !x  and  ~x  are not the same. What you're describing is  ~, not  !.

Thaddy

  • Hero Member
  • *****
  • Posts: 16199
  • Censorship about opinions does not belong here.
Re: C not and ternary conversion to pascal question.
« Reply #11 on: September 24, 2024, 01:46:07 pm »
C/C++ operator ! is logical negation, not bitwise, so !(any_nonzero_value) = 0. Take compiler and try, before writing.
no, i tried and added an example in the previous discussion: on higher, aggresive optimization levels clang uses bitwise. That was the core of the previous discussion. You should have known that I tested that... >:D
And you obviously did not!  >:D >:D >:D >:D
If I smell bad code it usually is bad code and that includes my own code.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: C not and ternary conversion to pascal question.
« Reply #12 on: September 24, 2024, 01:54:11 pm »
C/C++ has separate bitwise and boolean operators,  !x  and  ~x  are not the same. What you're describing is  ~, not  !.

That is true correct, and what's more K&R explicitly discussed the distinction and what it meant in the context of a machine word. However I side with Thaddy in this: while I've seen contrary assumptions buried deep within algorithms I think that you can safely assume that 0 is false and !0 is true, but you cannot assume that true is 1 (it might be -1, any value provided that the LSB is set and so on).

And as Thaddy's also said you have to beware of optimisation issues. And repeating myself, we don't know that compiler Jamie's original snippet was written for, and the values of the floating point numbers involved.

(That was written before Thaddy developed demonic tendencies :-)

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 16199
  • Censorship about opinions does not belong here.
Re: C not and ternary conversion to pascal question.
« Reply #13 on: September 24, 2024, 01:58:09 pm »
test code, so everybody can shut up and this can be closed:
Code: C  [Select][+][-]
  1. /*
  2.  * compiled like so:
  3.  * gcc -c -fPIC -o myclib.o myclib.c
  4.  * gcc -shared -fPIC myclib.o -o libmyclib.so
  5.  * chmod 755 libmyclib.so
  6.  * sudo cp libmyclib.so /usr/local/lib
  7.  * sudo ldconfig
  8.  */
  9. #include <stdio.h>
  10. #include <stdbool.h> /* optional in GNU 12 and higher */
  11. void mycfunc(_Bool arg){
  12.         printf("number: %d\n",arg?1:2);
  13. }
Code: Pascal  [Select][+][-]
  1. {$LINKLIB myclib}
  2. uses ctypes;
  3. procedure mycfunc(arg:ctypes.cbool);cdecl;external;
  4.  
  5. begin
  6.         mycfunc(false);
  7.         mycfunc(true);
  8. end.
Code: Bash  [Select][+][-]
  1. clang -c -fPIC -o myclib.o myclib.c
  2. clang -shared -fPIC myclib.o -o libmyclib.so
  3. chmod 755 libmyclib.so
  4. sudo cp libmyclib.so /usr/local/lib
  5. fpc -XXs -CX  prog.pas
  6. ./prog

Now compile the shared lib in clang or gnu C with -O1 and later -O4 and you will immediately see I am right. (the C code, not the pascal code)

That whole discussion was about the same thing and the only solution to avoid wrong code at higher optimizations is to work from false = 0 and true=not false.
This code is lifted from the bug report.
Code: Bash  [Select][+][-]
  1. # output O1 is correct
  2. number: 2
  3. number: 1
  4.  
  5. #output O4 is wrong
  6. number: 2
  7. number: -253
And that is the same issue as OP's issue.

« Last Edit: September 24, 2024, 02:38:25 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

tetrastes

  • Hero Member
  • *****
  • Posts: 600
Re: C not and ternary conversion to pascal question.
« Reply #14 on: September 24, 2024, 02:32:46 pm »
I've modified your example to C to avoid needless so/dll:
Code: C  [Select][+][-]
  1. /*
  2.  * compiled like so:
  3.  * gcc -c -fPIC -o myclib.o myclib.c
  4.  * gcc -shared -fPIC myclib.o -o libmyclib.so
  5.  * chmod 755 libmyclib.so
  6.  * sudo cp libmyclib.so /usr/local/lib
  7.  * sudo ldconfig
  8.  */
  9. #include <stdio.h>
  10. #include <stdbool.h> /* optional in GNU 12 and higher */
  11. void mycfunc(_Bool arg){
  12.         printf("number: %d\n",arg?1:2);
  13. }
  14.  
  15. int main()
  16. {
  17.         mycfunc(false);
  18.         mycfunc(true);
  19.         return 0;
  20. }
  21.  

See results at pic below with the whole process documented, so you'll not say that I did not do it.

And besides that, your code has nothing to do with ! operator.

 

TinyPortal © 2005-2018