If statements not treated as boolean? - if-statement

I'm a student, and my software teacher gave us this example -
BEGIN
IF first < second
THEN display first,second
ELSE
display second,first
ENDIF
END
If the two numbers, first and second were the same (say 2,2), the ELSE path would be taken, as first < second evaluates to false and so doesn't execute.
However, my software teacher said that in certain languages, both numbers being the same would be problematic and cause errors or strange behaviour (I believe he cited Visual Basic as an example). I do not see how this is possible. An IF statement is evaluated as either true or false and so one of the options MUST run and there should be no problem in evaluating whether 2 is less than 2.
Although he is my teacher and I respect him as such, I do not trust him completely and he does make errors at times. Is what he's said correct? And if so, could I have some specific examples of what happens?
Thanks.

Perhaps he is talking (in a round about way) about floating point imprecision?
there should be no problem in evaluating whether 2 is less than 2.
This is not always the case for some numbers stored using an imprecise representation, for example:
Dim first As Double, second As Double
first = 0.3
second = 0.1 + 0.2
If first < second Then
Debug.Print first, "is less than", second
Else
Debug.Print "equal or greater"
End If
Outputs:
0.3 is less than 0.3
See Is floating point math broken?
This can manifest more visibly when one exceeds the safe bounds of a floating point type, for example in JavaScript:
> 9007199254740992 == 9007199254740993
< true

As far as I know, the expression in if (expression) always evaluates to a boolean. At least that is true in C, C++, PHP, Java, .NET, Python, Javascript...
But maybe it is not the case in some old or less-used languages.
For Visual Basic, this Microsoft documentation page clearly says that number < number will evaluate to FALSE.

Well clearly a < a is false, if a is Integer(not fractional number). However in case of fractional number this may be true of false. Let me give you instance of each for c language. C compiler uses IEEE-754 number representation for floating point.
take a=0.1273(stored in memory as:0.1272999423027039)
take b=0.12 and c=0.0073+b
now if you check, c
It is true.

Related

FFmpeg compilation warnings ATOMIC_VAR_INIT

While compiling ffmpeg (commit 1368b5a) with emscripten 3.1.17, there are two warnings, I would like the internet to help me better understand (I am not someone with deep c++ experience):
fftools/ffmpeg.c:339:41: warning: macro 'ATOMIC_VAR_INIT' has been marked as deprecated [-Wdeprecated-pragma]
static atomic_int transcode_init_done = ATOMIC_VAR_INIT(0);
^
/home/XYZ/ffmpeg-wasm/modules/emsdk/upstream/lib/clang/15.0.0/include/stdatomic.h:50:41: note: macro marked 'deprecated' here
#pragma clang deprecated(ATOMIC_VAR_INIT)
^
I understand ATOMIC_VAR_INIT in this place is deprecated, but by which tool (emscripten, clang)? And which party to be involved in the fix.
The other one is also interesting:
fftools/ffmpeg_filter.c:898:35: warning: floating-point comparison is always true; constant cannot be represented exactly in type 'float' [-Wliteral-range]
if (audio_drift_threshold != 0.1)
~~~~~~~~~~~~~~~~~~~~~ ^ ~~~
The message and consequences are very clear. Whats not clear to me, is, if this is particular compiler issue, or code issue and which party to take care?
For ATOMIC_VAR_INIT I found this note:
This macro was a part of early draft design for C11 atomic types. It is not needed in C11, and is deprecated in C17 and removed in C23.
So, the deprecation was by the C standard (and the C++ standard, though there it was deprecated in C++20)
As for the floating point issue, the compiler states outright that:
constant cannot be represented exactly in type 'float'
Which is entirely true. In binary every bit of a number represents a power of two. In integers it is fairly easy, the bits represent 1,2,4,8,16,32,64,... matching 2 to the power of 0,1,2,3,4,5,6,...
For example the integer: 0b100101 is (low bit it to the right): 1+4+32=37.
Floating points are roughly the same, but with a few more complications (which I will skip here). Basically the bits now represent negative powers of two: 0.5, 0.25, 0.125, 0.0625, ... But unfortunately this does not allow us to represent all possible decimal numbers (this is an impossible task, there are infinitely many decimal numbers between 0 and 1, and we only have a finite number of combinations with a finite number of bytes).
It turns out that 0.1 is one of the numbers that it is impossible to encode in binary floating point. Which makes sense as 0.1 is 1/2 * 1/5, and 1/5 cannot be represented as a power of 2.
If you do the math, you find that:
2^-4 + 2^-5 + 2^-8 + 2^-9 + 2^-12 + 2^-13 = 0.0999755859375
If you continue this sequence forever (skip two powers, add two powers, repeat), then you will get infinitely close to 0.1. But that is the best we can do.
If you want to do some more reading, then here is some links:
What Every Programmer Should Know About Floating-Point Arithmetic
What Every Computer Scientist Should Know About Floating-Point Arithmetic
IEEE-754 Floating Point Converter
Now, the part I am a bit unsure of is:
warning: floating-point comparison is always true
Technically 0.1 should translate to some bit-pattern, and if you then throw a variable with that exact same bit pattern at the != check, then the result should be false. But perhaps the compiler made an executive decision and just said: "Always true", because getting the exact bit pattern is super unlikely and fragile.
But whatever the case, and more importantly: float != 0.1 is a really bad test. The links I put above elaborate on it much better than I can, but basically you don't ever want to test a floating point for strict equality - you always want to add an epsilon of some value (it depends on the numbers you compare).
For example: abs(float - 0.1) < epsilon.

Double/float comparison with exact values

There are a plenty of documents and discussions about float number comparison. But as for me it is not clear can it be always guaranteed that direct comparison of numbers will work on all compilers and platforms?
double x = 1.;
if (1. == x)
{
//do something
}
Will we always enter the if block?
Edited:
And what comparison here is correct (will work always)? This one?:
double x = 1.;
if (std::abs(1. - x) < std::numeric_limits<double>::epsilon())
{
//do something
}
Yes, direct comparison like that -- with no intervening operations -- will always work. The bit pattern that is stored for a floating point literal is the closest one representable by the floating point system (almost always IEEE-754). So testing 1.0 == 1.0 will always work because the bit pattern is that of 1.0; and 0.3 == 0.3 will also always work, because the bit pattern -- while not exactly 0.3 -- is the closest representable number to 0.3, in both cases.
As for the epsilon thing, stay away from machine epsilon until you actually know what it represents and what it's for. Machine epsilon is relative, not absolute; and using it to compare "close enough" requires an understanding of how much error various operations can introduce. Interestingly, in your particular case, the two tests are actually identical in effect: only exactly 1.0 will pass the test.
If you have two exact floating point values, you don't have to worry that comparing them may work contrary to your expectations. The problem is how do you know that your values are exact?
For the provided example you can be pretty confident that x == 1.0 will evaluate to true - I personally wouldn't consider supporting any platform that would fail that test. Yet, it is possible to gradually sophisticate your example without being able to tell for sure at which point you should stop relying on the outcome of the comparison.

Is it ok to compare floating points to 0.0 without epsilon?

I am aware, that to compare two floating point values one needs to use some epsilon precision, as they are not exact. However, I wonder if there are edge cases, where I don't need that epsilon.
In particular, I would like to know if it is always safe to do something like this:
double foo(double x){
if (x < 0.0) return 0.0;
else return somethingelse(x); // somethingelse(x) != 0.0
}
int main(){
int x = -3.0;
if (foo(x) == 0.0) {
std::cout << "^- is this comparison ok?" << std::endl;
}
}
I know that there are better ways to write foo (e.g. returning a flag in addition), but I wonder if in general is it ok to assign 0.0 to a floating point variable and later compare it to 0.0.
Or more general, does the following comparison yield true always?
double x = 3.3;
double y = 3.3;
if (x == y) { std::cout << "is an epsilon required here?" << std::endl; }
When I tried it, it seems to work, but it might be that one should not rely on that.
Yes, in this example it is perfectly fine to check for == 0.0. This is not because 0.0 is special in any way, but because you only assign a value and compare it afterwards. You could also set it to 3.3 and compare for == 3.3, this would be fine too. You're storing a bit pattern, and comparing for that exact same bit pattern, as long as the values are not promoted to another type for doing the comparison.
However, calculation results that would mathematically equal zero would not always equal 0.0.
This Q/A has evolved to also include cases where different parts of the program are compiled by different compilers. The question does not mention this, my answer applies only when the same compiler is used for all relevant parts.
C++ 11 Standard,
§5.10 Equality operators
6 If both operands are of arithmetic or enumeration type, the usual
arithmetic conversions are performed on both operands; each of the
operators shall yield true if the specified relationship is true and
false if it is false.
The relationship is not defined further, so we have to use the common meaning of "equal".
§2.13.4 Floating literals
1 [...] If the scaled value is in the range of representable values
for its type, the result is the scaled value if representable, else
the larger or smaller representable value nearest the scaled value,
chosen in an implementation-defined manner. [...]
The compiler has to choose between exactly two values when converting a literal, when the value is not representable. If the same value is chosen for the same literal consistently, you are safe to compare values such as 3.3, because == means "equal".
Yes, if you return 0.0 you can compare it to 0.0; 0 is representable exactly as a floating-point value. If you return 3.3 you have to be a much more careful, since 3.3 is not exactly representable, so a conversion from double to float, for example, will produce a different value.
correction: 0 as a floating point value is not unique, but IEEE 754 defines the comparison 0.0==-0.0 to be true (any zero for that matter).
So with 0.0 this works - for every other number it does not. The literal 3.3 in one compilation unit (e.g. a library) and another (e.g. your application) might differ. The standard only requires the compiler to use the same rounding it would use at runtime - but different compilers / compiler settings might use different rounding.
It will work most of the time (for 0), but is very bad practice.
As long as you are using the same compiler with the same settings (e.g. one compilation unit) it will work because the literal 0.0 or 0.0f will translate to the same bit pattern every time. The representation of zero is not unique though. So if foo is declared in a library and your call to it in some application the same function might fail.
You can rescue this very case by using std::fpclassify to check whether the returned value represents a zero. For every finite (non-zero) value you will have to use an epsilon-comparison though unless you stay within one compilation unit and perform no operations on the values.
As written in both cases you are using identical constants in the same file fed to the same compiler. The string to float conversion the compiler uses should return the same bit pattern so these should not only be equal as in a plus or minus cases for zero thing but equal bit by bit.
Were you to have a constant which uses the operating systems C library to generate the bit pattern then have a string to f or something that can possibly use a different C library if the binary is transported to another computer than the one compiled on. You might have a problem.
Certainly if you compute 3.3 for one of the terms, runtime, and have the other 3.3 computed compile time again you can and will get failures on the equal comparisons. Some constants obviously are more likely to work than others.
Of course as written your 3.3 comparison is dead code and the compiler just removes it if optimizations are enabled.
You didnt specify the floating point format nor standard if any for that format you were interested in. Some formats have the +/- zero problem, some dont for example.
It is a common misconception that floating point values are "not exact". In fact each of them is perfectly exact (except, may be, some special cases as -0.0 or Inf) and equal to s·2e – (p – 1), where s, e, and p are significand, exponent, and precision correspondingly, each of them integer. E.g. in IEEE 754-2008 binary32 format (aka float32) p = 24 and 1 is represented as ‭0x‭800000‬‬·20 – 23. There are two things that are really not exact when you deal with floating point values:
Representation of a real value using a FP one. Obviously, not all real numbers can be represented using a given FP format, so they have to be somehow rounded. There are several rounding modes, but the most commonly used is the "Round to nearest, ties to even". If you always use the same rounding mode, which is almost certainly the case, the same real value is always represented with the same FP one. So you can be sure that if two real values are equal, their FP counterparts are exactly equal too (but not the reverse, obviously).
Operations with FP numbers are (mostly) inexact. So if you have some real-value function φ(ξ) implemented in the computer as a function of a FP argument f(x), and you want to compare its result with some "true" value y, you need to use some ε in comparison, because it is very hard (sometimes even impossible) to white a function giving exactly y. And the value of ε strongly depends on the nature of the FP operations involved, so in each particular case there may be different optimal value.
For more details see D. Goldberg. What Every Computer Scientist Should Know About Floating-Point Arithmetic, and J.-M. Muller et al. Handbook of Floating-Point Arithmetic. Both texts you can find in the Internet.

assertions on negations of relational operators for floating point number

Suppose I have two variables a and b, either both in type float, or both in type double, which hold some values. Do the following assertions always hold? I mean, do the existence of numerical errors alter the conclusions?
a > b is true if and only if a <= b is false
a < b is true if and only if a >= b is false
a >= b is necessarily true if a == b is true
a <= b is necessarily true if a == b is true
For the third and fourth, I mean for example, does "a == b is true" always give you "a >= b is true"?
EDIT:
Assume neither a or b is NaN or Inf.
EDIT 2:
After reading the IEEE 754 standard in the year 1985, I found the following.
First of all, it said the following
Comparisons are exact and never overflow nor underflow.
I understand it as when doing comparison, there is no consideration of numerical error, numbers are compared as-is. Since addition and subtraction such as a - b requires additional effort to define what the numerical error is, I assume the quote above is saying that comparisons like a > b is not done by judging whether a - b > 0 is true or not. Please let me know if I'm wrong.
Second, it listed the four canonical relations which are mutually exclusive.
Four mutually exclusive relations are possible: less than, equal, greater than, and unordered. The last case arises when at least one operand is NaN. Every NaN shall compare unordered with everything, including itself.
Then in Table 4, it defined the various kinds of operators such as ">" or ">=" in terms of the truth values under these four canonical relations. From the table we immediately have the following:
a >= b is true if and only if a < b is false
a <= b is true if and only if a > b is false
Both a >= b and a <= b are necessarily true if a == b is true
So the assertions in my questions can be concluded as true. However, I wasn't able to find anything in the standard defining whether symmetricity is true or not. In another way, from a > b I don't know if b < a is true or not. Thus I also have no way to derive a <= b is true from b < a is false. So I would be interested to know, in addition to the assertions in the OP, whether the following is always true or not
a < b is true if and only if b > a is true
a <= b is true if and only if b >= a is true
etc.
EDIT 3:
Regarding denormal numbers as mentioned by #Mark Ransom, I read the wikipedia page about it and my current understanding is that the existence of denormal numbers does not alter the conclusions above. In another way, if some hardware claims that it fully support denormal numbers, it also needs to ensure that the definitions of the comparison operators satisfy the above standards.
EDIT 4:
I just read the 2008 revision of IEEE 754, it doesn't say anything about symmetricity either. So I guess this is undefined.
(All the above discussion assumes no NaN or Inf in any of the operands).
If neither number is a NaN or infinity then your assertions hold, if you have an IEEE compatible system. If the standards don't mention it then I'm sure that is just because it was sufficiently obvious to not be worth mentioning. In particular, if "a < b" and "b > a" don't have the same value (even for NaNs and infinities) then we are in crazy town.
Denormals shouldn't affect the conclusions because if you are assuming IEEE compatibility then denormal support is a given.
The only risks I can think of are in cases involving the x87 FPU and it's odd 80-bit long double format. Rounding from 80-bit long double to double or float is expensive, so it is sometimes omitted, and that can lead to an assert like this firing:
assert(sqrt(val) == sqrt(val));
It could fire because the result of the first sqrt() call may be written to memory, and therefore rounded to float or double. It would then be compared against the result of the second sqrt() call, which might not be rounded. However, failing to round the result of the second sqrt() call before doing the comparison is, strictly speaking, not IEEE compliant if sqrt() returns float or double.
Infinity should only be a problem if both numbers are infinity.

C/C++: Float comparison speed

I am checking to make sure a float is not zero. It is impossible for the float to become negative. So is it faster to do this float != 0.0f or this float > 0.0f?
Thanks.
Edit: Yes, I know this is micro-optimisation. But this is going to be called every time through my game loop, and I would like to know anyway.
There is not likely to be a detectable difference in performance.
Consider, for entertainment purposes only:
Only 2 floating point values compare equal to 0f: zero and negative zero, and they differ only at 1 bit. So circuitry/software emulation that tests whether the 31 non-sign bits are clear will do it.
The comparison >0f is slightly more complicated, since negative numbers and 0 result in false, positive numbers result in true, but NaNs (of both signs) also result in false, so it's slightly more than just checking the sign bit.
Depending on the floating point mode, either operation could cause a super-precise result in a floating point register to be rounded to 32 bit before comparison, so the score's even there.
If there was a difference at all, I'd sort of expect != to be faster, but I wouldn't really expect there to be a difference and I wouldn't be very surprised to be wrong on some particular implementation.
I assume that your proof that the value cannot be negative is not subject to floating point errors. For example, calculations along the lines of 1/2.0 - 1/3.0 - 1/6.0 or 0.4 - 0.2 - 0.2 can result in either positive or negative values if the errors happen to accumulate rather than cancelling, so presumably nothing like that is going on. About only real use of a floating-point test for equality with 0, is to test whether you have assigned a literal 0 to it. Or the result of some other calculation guaranteed to have result 0 in float, but that can be tricksy.
It is not possible to give a clear cut answer without knowing your platform and compiler. The C standard does not define how floats are implemented.
On some platforms, yes, on other platforms, no.
If in doubt, measure.
As far as I know, f != 0.0f will sometimes return true when you think it should be false.
To check whether a float number is non-zero, you should do Math.abs(f) > EPSILON, where EPSILON is the error you can tolerate.
Performance shouldn't be a big issue in this comparison.
This is almost certainly the sort of micro-optimization you shouldn't do until you have quantitative data showing that it's a problem. If you can prove it's a problem, you should figure out how to make your compiler show the machine instructions it's generating, then take that info and go to the data book for the processor you are using, and look up the number of clock cycles required for alternative implementations of the same logic. Then you should measure again to make sure you are seeing the benefits, if any.
If you don't have any data showing that's it's a performance problem stick with the implementation that most clearly and simply presents the logic of what you are trying to do.