So between 2^63 and 2^64-1, you get negative integers showing you how far off from ULONG_MAX you are.
No. How do you figure that? By your own example, the max is:
> max=$((2**63 - 1)); echo $max
9223372036854775807
If "overflow" meant "you get negative integers showing you how far off from ULONG_MAX you are", then if we add one to that, shouldn't we get -1? But instead:
> echo $(($max + 1))
-9223372036854775808
Perhaps you mean this is a number you can add to $max
to get a negative difference, since:
> echo $(($max + 1 + $max))
-1
But this does not in fact continue to hold true:
> echo $(($max + 2 + $max))
0
This is because the system uses two's complement to implement signed integers.1 The value resulting from an overflow is NOT an attempt to provide you with a difference, a negative difference, etc. It is literally the result of truncating a value to a limited number of bits, then having it interpreted as a two's complement signed integer. For example, the reason $(($max + 1 + $max))
comes out as -1 is because the highest value in two's complement is all bits set except the highest bit (which indicates negative); adding these together basically means carrying all the bits to the left so you end up with (if the size were 16-bits, and not 64):
11111111 11111110
The high (sign) bit is now set because it carried over in the addition. If you add one more (00000000 00000001) to that, you then have all bits set, which in two's complement is -1.
I think that partially answers the second half of your first question -- "Why are the negative integers...exposed to the end user?". First, because that is the correct value according to the rules of 64-bit two's complement numbers. This is the conventional practice of most (other) general purpose high level programming languages (I cannot think of one that does not do this), so bash
is adhering to convention. Which is also the answer to the first part of the first question -- "What's the rationale?": this is the norm in the specification of programming languages.
WRT the 2nd question, I have not heard of systems which interactively change ULONG_MAX.
If someone arbitrarily changes the value of the unsigned integer maximum in limits.h, then recompiles bash, what can we expect will happen?
It would not make any difference to how the arithmetic comes out, because this is not an arbitrary value that is used to configure the system -- it's a convenience value that stores an immutable constant reflecting the hardware. By analogy, you could redefine c to be 55 mph, but the speed of light will still be 186,000 miles per second. c is not a number used to configure the universe -- it's a deduction about the nature of the universe.
ULONG_MAX is exactly the same. It is deduced/calculated based on the nature of N-bit numbers. Changing it in limits.h
would be a very bad idea if that constant is used somewhere assuming it is supposed to represent the reality of the system.
And you cannot change the reality imposed by your hardware.
1. I don't think that this (the means of integer representation) is actually guaranteed by bash
, since it depends on the underlying C library and standard C does not guarantee that. However, this is what is used on most normal modern computers.
=
is the same as ==
when inside [[...]]
. As per the more recent man
page, under SHELL GRAMMAR
> Compound Commands
> [[ expression ]]
:
The = operator is equivalent to ==
and further down, under CONDITIONAL EXPRESSIONS
:
string1 == string2
string1 = string2
True if the strings are equal. = should be used with the test command
for POSIX conformance. When used with the [[ command, this performs pattern
matching as described above (Compound Commands).
bash info
page:
Best Answer
It's very simple. You just have to understand
[]
,[[]]
and(())
as completely different beasts.[ expression ]
is just an invocation of a program[
(just a different name fortest
) and takes the expression as arguments, just like for all command calls in bash. That means, you must use whitespace between arguments, especially after[
and before]
, and keywords and special characters have to be escaped or quoted, and variables are expanded in the usual way.Within the expression,
-gt, -lt
and related are used for numerical comparison, while>, <
and the sort, are for string comparisons. Think of[
as nothing to do with bash, just calling a command that evaluates expressions and returns 0 (true) or non-zero (false), as all programs do.Boolean operators
&&
and||
can't be used in there, because they get interpreted by bash and not passed totest
as arguments.-a
and-o
are used instead. But you can always do[ test1 ] && [ test2 ] || [ test3 ]
, which are 3 test invocations, combined bybash
's usual boolean operators (that is, if first fails, the second is skipped).Note that
bash
still comes with its own implementation of[
(builtin), but that doesn't change the way it's treated syntactically.[[ ]]
and(( ))
are not builtin replacements for commands, but parts of a special syntax, so different rules apply. See man page for details on how variable names are wildcards are treated there. Boolean operators&&
and||
have the usual meaning.The
[[ ]]
one is string-centered: all comparsions are for strings (but are more "modern", locale-aware than in[
, althoughtest
could be platform-dependent). It handles also file tests, regex and so on.The
(( ))
is for arithmetic expressions. Variable names don't require$
there, and you can mostly just write mathematical expressions in there - that of course includes comparisons (which are numerical). Comparisons in this case are no different from any other arithmetic expression, true is 1, false is 0, and you can write stuff like(( x=y*(z>4) ))
. If used in a conditional expression, nonzero is true and zero is false. You can also capture the result as$(( ))
.So:
[[ ]]
string conditionals, pattern matching and file tests(( ))
arithmetic expressions and conditionals[ ]
test command: handles both, special nonstandard syntax for boolean operators and comparison operators