2
$\begingroup$

What I want to ask is not the Boolean value at the type level, but the Boolean value in actual implementation.

Because the actual register cannot store a bit, and the register has no type, we need to consider how to reinterpret the value according to the (user typed) Boolean type constraints when taking out the value in the register.

Most mainstream languages ​​continue the mathematical definition, assuming that 0 is false and other values ​​represent true.

Boolean values ​​are so commonly used that incorrect design may have disastrous consequences.

So question is: I want to know if there are any special troubles if I use a different definition (such as 0 represents true and 1 represents false)?


Here are some opinions:

  • Mathematics usually uses 0 to represent false and 1 to represent true
    • But mathematics does not consider the situation where there are other numbers
  • Some mathematicians believe that 0 and -1 should be used from the perspective of truth tables
    • I don't think negative numbers are a good idea, because there will be fatal problems when converting between signed and unsigned numbers.
  • Some languages ​​only look at the last bit, if it is 0, it means false, otherwise it means true
  • The solution adopted by the shell is 0 to represent true and other values ​​to represent false
    • Because there is only one perfect success, and there are various failures
  • ...

There are also some views from the computer perspective

  • CPU initialization is all 0 initialization, 0 is the default value, the default value should be false
    • But if I set true as the default value, it seems that there will be no difference.
  • eqz-jmp is a common jump mode
  • Mainstream FFIs all default 0 to false
    • Indeed, there will be additional conversion loss at the FFI call site.
    • But FFI calls already have a lot of losses, and a normalization instruction is not fatal
$\endgroup$
5
  • 5
    $\begingroup$ I don't really understand your objections to -1. What are the "fatal problems" that you foresee? Can you give an example? Forth is a language that uses -1 for TRUE and it doesn't seem to cause any particular issues. $\endgroup$ Commented May 26 at 17:04
  • $\begingroup$ "Because the actual register cannot store a bit" - since you're talking about an actual implementation, which instruction set architecture are you talking about? $\endgroup$
    – Bergi
    Commented May 26 at 22:10
  • 2
    $\begingroup$ I would argue the "boolean" interpreted by the shell is the answer to the question "Was there an error (and what was it)?" so, although 0 means success and nonzero means failure, I wouldn't say "0 means true" and "nonzero means false". $\endgroup$
    – Stef
    Commented May 29 at 13:44
  • $\begingroup$ @stef: but "0 true and nonzero false" are how the control structures work (if, while, etc) $\endgroup$ Commented May 29 at 17:01
  • $\begingroup$ @Stef: I agree. Perhaps the Unix developers confused things by having commands named true and false instead of succeed and fail. Exit codes are not bools. $\endgroup$
    – dan04
    Commented Jun 11 at 20:21

5 Answers 5

11
$\begingroup$

I think your question is a false dichotomy, at least in a lot of cases; modern high-level languages1 tend not to allow conversion between booleans and integer types, so this should be considered an implementation detail and it doesn't matter how a specific implementation happens to do it. Taking this further, "register" is an implementation detail as well.

1. C and C++ are not "modern" in this context!

$\endgroup$
8
  • 1
    $\begingroup$ What languages do you consider to be modern and high level? I can't think of any mainstream ones that don't have true + 1 = 2 except maybe Rust. Though I don't really know any functional languages so maybe it's more common in that field $\endgroup$
    – mousetail
    Commented May 26 at 11:12
  • 11
    $\begingroup$ None of Java, C# nor Go will allow that! $\endgroup$ Commented May 26 at 11:15
  • 1
    $\begingroup$ @mousetail In general, it seems like there is a significant push for statically typed languages to move more and more towards type safety (which would usually prevent that). $\endgroup$ Commented May 26 at 15:59
  • 5
    $\begingroup$ PHP and Python both allow it. In Python, bool is a subtype of int. Whatever your personal preferences may be, these are two of the most popular high-level languages these days. $\endgroup$
    – Barmar
    Commented May 27 at 22:23
  • 1
    $\begingroup$ @PhilipKendall, the interesting question isn't "does the language allow true + 1?", the interesting question is "does the language allow if(n + 3)?". $\endgroup$
    – Mark
    Commented Jun 11 at 1:13
6
$\begingroup$

Because the actual register cannot store a bit, and the register has no type,

You are probably thinking about a general purpose register. However, CPUs typically also have flags to store certain results, and those flags are typically one bit containing a true/false value. Many CPU architectures have something like a CMP (compare) instruction, which can compare two values for equality, and which stores the result in one of those flags. Some CPUs allow those flags to be accessed directly, some only indirecly (like with JE/JNE (jump if equal/not equal) instructions), and some present a collection of flags as a single status register. Still, you can think of the flag as a single boolean register.

It's also not right to say that register don't have a type. You typically have different types of registers, some for integers, some for floating points. In hardware, there is typically a set of registers that are all 32 or 64 bits, but the instruction set might expose logical registers that are 8 or 16 bits wide. At the hardware level there are also no ones and zeroes, it's some amount of charge or voltage, or the absence of it, and it doesn't even have to be that a high voltage means a 1.

Mathematics usually uses 0 to represent false and 1 to represent true

I think it's computer science that does that, not mathematics.

Some mathematicians believe that 0 and -1 should be used from the perspective of truth tables

Some operations on booleans have an equivalent in operations on integers. For example, if you use 0 for false and 1 for true, then a logical AND is equivalent to multiplication of those integer values. You can then also see that this no longer works if you use 0 for true and 1 for false.

When working with registers of a fixed width, you are working in a ring with modular arithmetic, and usually signed numbers are stored in two's complement. That means -1 is equivalent to all bits in that register being 1. This can make some things easier, for example in the following piece of C code:

bool a = …;
int x = 123;
int y = a ? x : 0;

The compiler could then replace the conditional expression with a bitwise AND operation:

int y = x & (int)a;

Which could result in much faster assembly being generated. However, since using just 1 and 0 for true and false is so common, most CPUs provide instructions to handle that as fast as storing booleans as -1 and 0 would.

Also, note that in C and languages that use the same ABI, 0 is false but when converting an int or other similar type to bool, any non-zero value is considered true. And while the ABI says that a boolean is stored or passed as a parameter, the value should be exactly 1 or 0, the compiler is allowed to do anything it wants behind the scenes as long as the observable end result is the same as what the language specification says. So it might actually store true as a non-zero value other than 1 if that is more expedient and nothing would break.

The solution adopted by the shell is 0 to represent true and other values ​​to represent false

Not exactly; the result of a command is not a boolean, it is an error code, where 0 means no error. The same goes for errno.

Mainstream FFIs all default 0 to false

Because C defined false to be zero, and most FFIs use C's ABI.

CPU initialization is all 0 initialization,

Not necessarily, and especially not the memory. A BIOS or operating system's first task is usually to zero all memory. The CPU has defined reset values for some of the registers, and some of those reset values might not be zero. When a program is started by the operating system, it's also not the case that all registers are zero.

What is true is that often the CPU provides a fast way to set a register to zero, either by having specific instructions for that or by having a special register that is always zero, and that you can copy into other registers. And even if there is nothing explicit, you can almost always XOR a register with itself.

$\endgroup$
3
  • 1
    $\begingroup$ Actually, many if not all C and C++ ABIs specify that bool values must be passed and stored as 0 or 1, nothing else. This allows functions to use bitwise operations on bool inputs without spending extra instructions to coerce to 0/1. Of course this is unrelated to the language semantics that nonzero values of other types are "truthy". $\endgroup$ Commented May 26 at 17:10
  • 2
    $\begingroup$ Thanks to de Morgan's law, if we take 0 for true and 1 for false, we can still use bitwise logic instructions to do Boolean logic: now boolean AND corresponds to bitwise OR, and vice versa. $\endgroup$ Commented May 26 at 17:16
  • 2
    $\begingroup$ I should rephrase that. While I know the ABI says bool must be passed as 0 or 1, the as-if rule applies as well; if the compiler can see how and where things are passed, it can do things differently behind the scenes, as long as the end result is the same. And when converting an int to a bool for example, any non-zero value int value becomes true. $\endgroup$
    – G. Sliepen
    Commented May 26 at 17:41
0
$\begingroup$

As long as the code stays "inside" the type safe language you have created it does not really make much difference. In a fully type safe language there should be no way for the program to extract the actual representation. Each and every access to the boolean should pass through the type system which should protect the internal representation. There should be no type-unsafe conversions from/to boolean to say integer. Adding an integer and a boolean should either trigger a compilation error or trigger a type conversion with exact definition. C is notoriously bad when it comes to this, it is more or less an addition to the assembler. You are allowed to do a lot of not type-safe stuff with booleans, such as AND with a bit mask on a packed boolean bit field.

In theory your language could have different representations on different hardware platforms - the later stage of compilation will generally translate into actual hardware instructions (especially if the code is interpreted). This is possible as every translation to/from boolean should be type safe. It should not be possible to do an unsafe store of an integer into a boolean as one example.

The effect of the choice you make will show itself in some other areas. There could be a performance penalty on some CPU architectures depending on your choice. My personal guess is that the difference will be minute, but the only way to know is to measure the difference on larger actual software systems.

You also have to consider what happens when the boolean leaves the "inside" of the type controlled system. This can be when calling external code as in linking in external libraries. Other examples include storing data to external files or when interpreting data read from other programs.

So, it all boils down to your choices as language designer. You need to start on how you want the type conversions to work - what happens when you assign as example a real to a boolean? Should it be allowed and when should it translate into true/false.

One additional complication is if you want to add a "not-a-boolean" value to your type, as in the "not-a-number" we have for real values.

If you want my suggestion: do as others have done. Let 0 be false and anything else be true. When translating from boolean to other types, translate true to 1.

$\endgroup$
-1
$\begingroup$

Logically, it doesn't matter which two values you take as long as your logical operations are programmed appropriately. Use bathroom sinks and kitchen sinks, for instance :-).

Physically, 0 (bit pattern 0…0) and -1 (bit pattern 1…1) result in the most stable storage under two assumptions: the two's complement representation is used, and the bits are stored independently. No memory can hide from the radiation of space completely; all DRAM modules (even with ECC) sooner or later experience bitflips (inevitably, incorrectable), even on the ground.

An example of a usage of 0 and -1 is SQL in MS Access: there, TRUE is -1, and FALSE is 0.

Two asides are worth being made.

First, you might also wonder about other pairs of bit patterns with the same maximal distance, say, 01010101 and 10101010 for 8-bit words. Then you'd have to use comparison with a constant instead of testing for the majority of zeros or ones. The the simple bitcount test (“does a given byte have at least 4 ones?”) would be replaced by the more complicated counting the number of positions with a deviation from one of the two constants (“does a given byte coincide with the constant on at least 4 positions?”). So I don't advise other bit patterns (unless there are other, independent reasons).

Second, as for the faults in rotational media and in transmissions over long distances, according to my own recollection, the bits need not be stored independently, the faults in them may be correlated, and bits may be skipped/inserted, so before changing your Boolean values to anything to increase tolerance to physical faults, take a look at the actual way the data is stored or transmitted in your application.

$\endgroup$
14
  • $\begingroup$ Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center. $\endgroup$ Commented May 30 at 0:51
  • $\begingroup$ I don't think a citation is necessary to establish that setting all 1 bits and all 0 bits makes the values maximally different in their representation, and thus "most stable". $\endgroup$ Commented May 30 at 1:20
  • $\begingroup$ If you use 0 for false and -1 for true, then a bitflip will give you some value that equals neither. Say a bit is flipped in a 0 value and gives you 1024. Do you treat that as false as well? This suggests a convention where any value with the majority of bits set to 1 is treated as true, but that's going to be quite expensive to implement on a typical computer, especially if you don't have a hardware popcount instruction. Do you know of any existing language or implementation where this is actually done? $\endgroup$ Commented May 30 at 2:47
  • $\begingroup$ Or do you treat 1024 as invalid, and insert frequent tests for such invalid values? $\endgroup$ Commented May 30 at 2:48
  • 1
    $\begingroup$ If error-correction from cosmic ray bitflips is required, then it needs to be provided at the hardware level, not by the programming language for just one data type. Your argument that ECC memory alone cannot protect against all corruption, doesn't imply that we should implement redundant error-correction in software; that would still not protect against all corruption, for the same reason. Either way, you have to set a probability of failure low enough that you accept it. Then it makes much more sense to just choose ECC RAM which achieves that low probability. $\endgroup$
    – kaya3
    Commented May 30 at 12:50
-2
$\begingroup$

Using a unique Boolean definition, where zero represents actual and 1 represents false, can introduce confusion and workable blunders in code interpretation, especially when participating with others or using libraries that observe the traditional definition. It may also lead to surprising behaviors and make code tougher to recognize and maintain. Therefore, deviating from the fashionable Boolean definition can cause compatibility problems and increase the threat of bugs in software program development.

$\endgroup$
1
  • $\begingroup$ Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center. $\endgroup$ Commented Jun 2 at 18:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .