User avatar
HermannSW
Posts: 4236
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 5:43 pm

I looked into C++ code that tests two values v1 and v2 for equality.
The double case did just this -- and that was the bug I did hunt for:

Code: Select all

...
else if (v1->mType == kDouble) 
  return v1->mValue.mDouble == v2->mValue.mDouble;
else if ...

Why is that a bug?
If both v1 and v2 are NaN, then false gets returned.
This simple change fixed the issue, now comparing all doubles returns true if equal:

Code: Select all

  return isnan(v1->mValue.mDouble) ? isnan(v2->mValue.mDouble) : v1->mValue.mDouble == v2->mValue.mDouble;
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://github.com/Hermann-SW/raspiraw
https://stamm-wilbrandt.de/en/Raspberry_camera.html

trejan
Posts: 3262
Joined: Tue Jul 02, 2019 2:28 pm

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 5:58 pm

HermannSW wrote:
Fri Jun 18, 2021 5:43 pm
Why is that a bug?
If both v1 and v2 are NaN, then false gets returned.
This isn't a bug. One NaN can't be compared against another NaN as they're both undefined values hence not a number.
https://www.gnu.org/software/libc/manual/html_node/Infinity-and-NaN.html wrote: NaN is unordered: it is not equal to, greater than, or less than anything, including itself.
Comparing doubles for equality is problematic anyway so beware.

User avatar
HermannSW
Posts: 4236
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 6:05 pm

I know that, but the code has to return true if both v1 and v2 are NaN, and the small fix does just that.
The requirement for two NaN values being equal is in a compiler for another language ...
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://github.com/Hermann-SW/raspiraw
https://stamm-wilbrandt.de/en/Raspberry_camera.html

User avatar
jahboater
Posts: 7150
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 6:12 pm

HermannSW wrote:
Fri Jun 18, 2021 6:05 pm
I know that, but the code has to return true if both v1 and v2 are NaN, and the small fix does just that..
The test for a NaN is "if( x != x ) then it is a NaN" - which is I think what isnan() does.

This is nothing to do with the compiler or C++, it is what the IEEE 754 floating point arithmetic hardware does.

dsyleixa123
Posts: 1492
Joined: Mon Jun 11, 2018 11:22 am

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 6:25 pm

HermannSW wrote:
Fri Jun 18, 2021 6:05 pm
I know that, but the code has to return true if both v1 and v2 are NaN, and the small fix does just that.
The requirement for two NaN values being equal is in a compiler for another language ...
[SmartAssMode /ON]
mathematically speaking, equality of values by a mathematical operator (==, !=) is only defined for values (e.g. numerical or boolean values), not for something other than values, and nan is not a value (number) by definition.
But you may compare if isnan(value1)==isnan(value2) or if isnan(value1)==true because isnan returns boolean values which can be checked for equality.
https://www.cplusplus.com/reference/cmath/isnan/
[SmartAssMode /OFF]
:mrgreen:

User avatar
jahboater
Posts: 7150
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 6:35 pm

NaN's have a very large number of values (there is a 51 bit "payload"). Also there are quiet and signalling NaN's.

union { double r; uint64_t d; } value;

if( value1.d == value2.d ) ....

User avatar
HermannSW
Posts: 4236
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 6:54 pm

The requirement was from XML Schema compiler, that incorrectly did not validate the NaN element <foobar> against fixed xs:double value NaN. This online XML Schema validator does correctly validate:
https://www.liquid-technologies.com/onl ... -validator
NaN.jpg
NaN.jpg
NaN.jpg (38.05 KiB) Viewed 949 times
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://github.com/Hermann-SW/raspiraw
https://stamm-wilbrandt.de/en/Raspberry_camera.html

Heater
Posts: 18335
Joined: Tue Jul 17, 2012 3:02 pm

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 7:00 pm

HermannSW wrote:
Fri Jun 18, 2021 5:43 pm
Why is that a bug?
Never mind NaNs. It most likely a bug because comparing results of floating point calculations for equality is likely to produce an unexpected result.

See: https://docs.oracle.com/cd/E19957-01/80 ... dberg.html
HermannSW wrote:
Fri Jun 18, 2021 5:43 pm
The requirement for two NaN values being equal is in a compiler for another language ...
Please do tell what other language that is. I cannot think of one of hand.
Memory in C++ is a leaky abstraction .

User avatar
HermannSW
Posts: 4236
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 7:09 pm

Heater wrote:
Fri Jun 18, 2021 7:00 pm
Please do tell what other language that is. I cannot think of one of hand.
XML Schema 1.0 language, as shown in screenshot in previous posting.
Here is definition for "xs:double" type in spec:
https://www.w3.org/TR/2004/REC-xmlschem ... tml#double
"xs:double" differs from IEEE 754-1985:
This datatype differs from that of [IEEE 754-1985] in that there is only one NaN and only one zero.
P.S:
The following sentence brings it to the point:
This makes the equality and ordering of values in the data space differ from that of [IEEE 754-1985] only in that for schema purposes NaN = NaN.
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://github.com/Hermann-SW/raspiraw
https://stamm-wilbrandt.de/en/Raspberry_camera.html

tttapa
Posts: 65
Joined: Mon Apr 06, 2020 2:52 pm

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 7:35 pm

jahboater wrote:
Fri Jun 18, 2021 6:35 pm
union { double r; uint64_t d; } value;

if( value1.d == value2.d ) ....
This is not valid C++, type punning using unions invokes undefined behavior. If you want to compare the bit pattern/object representation of a variable, use `std::memcmp`, if you want to reinterpret a double as a uint64_t, use `std::memcpy` or `std::bit_cast`.

User avatar
jahboater
Posts: 7150
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 9:30 pm

tttapa wrote:
Fri Jun 18, 2021 7:35 pm
jahboater wrote:
Fri Jun 18, 2021 6:35 pm
union { double r; uint64_t d; } value;

if( value1.d == value2.d ) ....
This is not valid C++, type punning using unions invokes undefined behavior. If you want to compare the bit pattern/object representation of a variable, use `std::memcmp`, if you want to reinterpret a double as a uint64_t, use `std::memcpy` or `std::bit_cast`.
It is valid C (from the C18 standard):
If the member used to read the contents of a union object is not the same as the member last used to store a value in the
object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new
type as described in 6.2.6 (a process sometimes called "type punning").
Strange that the languages differ in this respect!

Memcpy etc work for C as well of course.
Last edited by jahboater on Fri Jun 18, 2021 9:42 pm, edited 1 time in total.

Heater
Posts: 18335
Joined: Tue Jul 17, 2012 3:02 pm

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 9:42 pm

HermannSW wrote:
Fri Jun 18, 2021 7:09 pm
This datatype differs from that of [IEEE 754-1985] in that there is only one NaN and only one zero.
Interesting. Thank you for that.

So not 754 floats as implemented by most programming languages today.

Of course I could go on to ask if a text mark up language like XML is a programming language at all. And does anyone still use XML?

But never mind.
Memory in C++ is a leaky abstraction .

User avatar
jahboater
Posts: 7150
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 9:44 pm

Heater wrote:
Fri Jun 18, 2021 9:42 pm
HermannSW wrote:
Fri Jun 18, 2021 7:09 pm
This datatype differs from that of [IEEE 754-1985] in that there is only one NaN and only one zero.
Interesting. Thank you for that.

So not 754 floats as implemented by most programming languages today.
Presumably grossly inefficient as it all must be implemented in software.

The fact that there is only one zero implies its not even sign-magnitude.

Heater
Posts: 18335
Joined: Tue Jul 17, 2012 3:02 pm

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 9:57 pm

tttapa wrote:
Fri Jun 18, 2021 7:35 pm
jahboater wrote:
Fri Jun 18, 2021 6:35 pm
union { double r; uint64_t d; } value;

if( value1.d == value2.d ) ....
This is not valid C++, type punning using unions invokes undefined behavior. If you want to compare the bit pattern/object representation of a variable, use `std::memcmp`, if you want to reinterpret a double as a uint64_t, use `std::memcpy` or `std::bit_cast`.
As far as I can tell that code may or may not trigger undefined behavior depending on how value1 and value2 were written last, before that comparison is made.

Because according to the standard: "It is undefined behavior to read a union member with a different type from the one with which it was written. "

Which I assume means if they were both written with same types last all is good.

So if value1.d == value2.d were last written with valid doubles then the comparison "value1.d == value2.d" is just fine.

Undefined behavior happens at run time, when the code is used, not as you read it in your editor or as the compiler compiles it.
Memory in C++ is a leaky abstraction .

tttapa
Posts: 65
Joined: Mon Apr 06, 2020 2:52 pm

Re: TIL: "==" is not "is equal" for "double"

Fri Jun 18, 2021 10:39 pm

Heater wrote:
Fri Jun 18, 2021 9:57 pm
As far as I can tell that code may or may not trigger undefined behavior depending on how value1 and value2 were written last, before that comparison is made.

Because according to the standard: "It is undefined behavior to read a union member with a different type from the one with which it was written. "

Which I assume means if they were both written with same types last all is good.

So if value1.d == value2.d were last written with valid doubles then the comparison "value1.d == value2.d" is just fine.
Given that the goal of the snippet was to compare the bits of a double value, I think it's safe to assume that `value1.r` was last written, otherwise the union would be unnecessary here. (Note that value1.d is the integer value, not the double value.)

Code: Select all

union { double r; uint64_t d; } value1, value2; // bad, don't do this in C++
value1.r = 0.1;
value2.r = 0.1;
bool equal = value1.d == value2.d; // undefined behavior, member d wasn't written most recently
This invokes undefined behavior no matter what happens at run time, `equal` could evaluate to any value, even values other than `true` or `false`, the program could just crash, it might even become self-aware and start a nuclear war. (Admittedly, if you use a sensible compiler, the latter is rather unlikely, but who knows, the standard allows it and you can only blame the programmer, not the compiler :) )

User avatar
Paeryn
Posts: 3302
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 12:51 am

Heater wrote:
Fri Jun 18, 2021 9:57 pm
Because according to the standard: "It is undefined behavior to read a union member with a different type from the one with which it was written. "
That's old wording, as jahboater said earlier C18 states that the parts of the union holding the value for the reading member will be reinterpreted as if it were the type of the member. I said the same thing in another thread except I attributed it to C11, I just checked and even C99 has the footnote that the value will be reinterpreted.

I suppose it's undefined behaviour only in that you're not guaranteed to get the same value on a different system since C doesn't say how a type is formatted.
She who travels light — forgot something.
Please note that my name doesn't start with the @ character so can people please stop writing it as if it does!

Heater
Posts: 18335
Joined: Tue Jul 17, 2012 3:02 pm

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 4:16 am

So reading a union using a variant other than what was written last is undefined by C itself then. It's impossible to tell what will happen from reading the source alone.
Memory in C++ is a leaky abstraction .

User avatar
jahboater
Posts: 7150
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 5:07 am

Heater wrote:
Sat Jun 19, 2021 4:16 am
So reading a union using a variant other than what was written last is undefined by C itself then. It's impossible to tell what will happen from reading the source alone.
No, it is allowed in C. Here is the except from the C18 standard again:
If the member used to read the contents of a union object is not the same as the member last used to store a value in the
object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new
type as described in 6.2.6 (a process sometimes called "type punning").
What is frowned upon is:

double *ptr = &x;
uint64_t d = *(uint64_t*)ptr;

You should use memcpy, or a union instead.

Heater
Posts: 18335
Joined: Tue Jul 17, 2012 3:02 pm

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 7:26 am

jahboater wrote:
Sat Jun 19, 2021 5:07 am
Heater wrote:
Sat Jun 19, 2021 4:16 am
So reading a union using a variant other than what was written last is undefined by C itself then. It's impossible to tell what will happen from reading the source alone.
No, it is allowed in C. Here is the except from the C18 standard again:
If the member used to read the contents of a union object is not the same as the member last used to store a value in the
object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new
type as described in 6.2.6 (a process sometimes called "type punning").
It allowed. Only thing is the result is not defined. Depending on architecture and likely the compiler implementation. Also not allowed in C++.
Memory in C++ is a leaky abstraction .

User avatar
jahboater
Posts: 7150
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 8:20 am

Heater wrote:
Sat Jun 19, 2021 7:26 am
Depending on architecture and likely the compiler implementation.
Type "double" has a fixed format for all C implementations ISO/IEC 60559 (IEEE 754 binary 64).

Type "uint64_t" is defined to be a two's complement binary integer (defined by the language standard I believe).

True for all hardware and all compilers.

I have small amounts of code that relies on this, so please could you show me an example of modern architecture and/or C compiler where this is not true?

The endian'nes will be be the same of course on the same machine, though is trivial to check statically.

Code: Select all

/*
 *  True if x is a NaN or infinity.
 *  -ffinite-math-only causes GCC to elide isfinite/isnan/isinf.
 */
static inline pure bool
invalid( const double x )
{
  uint64_t q;
  memcpy(&q, &x, 8);
  return (q << 1) >> 53 == 0x7FF;
}

User avatar
Paeryn
Posts: 3302
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 8:32 am

The result is defined, but it's unspecified as to what that result is. If compiled using the same compiler on the same architecture then you should always get the same result. It isn't the unruly Undefined Behaviour, just its younger sibling Unspecified Behaviour who is well mannered and tries to be consistent.

It has to be unspecified as C makes no guarantee as to how a value is repesented therefore it can't say as to what a value will look like when you choose to interpret a value as type other than the type of the original value.
She who travels light — forgot something.
Please note that my name doesn't start with the @ character so can people please stop writing it as if it does!

Heater
Posts: 18335
Joined: Tue Jul 17, 2012 3:02 pm

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 8:54 am

jahboater wrote:
Sat Jun 19, 2021 8:20 am
Heater wrote:
Sat Jun 19, 2021 7:26 am
Depending on architecture and likely the compiler implementation.
Type "double" has a fixed format for all C implementations ISO/IEC 60559 (IEEE 754 binary 64).
Yep.
jahboater wrote:
Sat Jun 19, 2021 8:20 am
Type "uint64_t" is defined to be a two's complement binary integer...
Nope. two's comp is a representation of singed integers not unsigned. But I know what you mean.
jahboater wrote:
Sat Jun 19, 2021 8:20 am
... (defined by the language standard I believe).
True for all hardware and all compilers.
I'm not convinced about that. There was a proposal to make two's comp the standard representation in 2018: "N2218
Signed Integers are Two’s Complement": http://www.open-std.org/jtc1/sc22/wg14/ ... /n2218.htm

However you are using unsigned so no matter.
jahboater wrote:
Sat Jun 19, 2021 8:20 am
I have small amounts of code that relies on this, so please could you show me an example of modern architecture and/or C compiler where this is not true?
I suspect that as you are using unsigned 64 bit integer your are in good shape.
jahboater wrote:
Sat Jun 19, 2021 8:20 am
The endian'nes will be be the same of course on the same machine, though is trivial to check statically.
I wonder, does the C standard stimulate that endianness for ints has to be the same as endianness for floating point types?
jahboater wrote:
Sat Jun 19, 2021 8:20 am

Code: Select all

/*
 *  True if x is a NaN or infinity.
 *  -ffinite-math-only causes GCC to elide isfinite/isnan/isinf.
 */
static inline pure bool
invalid( const double x )
{
  uint64_t q;
  memcpy(&q, &x, 8);
  return (q << 1) >> 53 == 0x7FF;
}
It's a good idea to have such checks in place. Either at compile time. Or perhaps some initial code that runs on program start up and checks the environment it finds itself in is OK to continue.

Of course having to use magic, compiler specific switches like "-ffinite-math-only" brings us back to my claim that program behaviour cannot be determined from inspecting the source code. And it could change from machine to machine, compiler to compiler...
Memory in C++ is a leaky abstraction .

User avatar
jahboater
Posts: 7150
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 9:16 am

Heater wrote:
Sat Jun 19, 2021 8:54 am
jahboater wrote:
Sat Jun 19, 2021 8:20 am
Type "uint64_t" is defined to be a two's complement binary integer...
Nope. two's comp is a representation of singed integers not unsigned. But I know what you mean.
My bad. I wrote that too quickly! Yes I would hope that unsigned integers are the same everywhere.
Heater wrote:
Sat Jun 19, 2021 8:54 am
I'm not convinced about that. There was a proposal to make two's comp the standard representation in 2018: "N2218
Signed Integers are Two’s Complement": http://www.open-std.org/jtc1/sc22/wg14/ ... /n2218.htm
Interesting link.
Its more a case of "recognizing" or "acknowledging" that all known signed integer hardware is two's complement.
For C and C++ therefore, overflow is no longer UB. C++ is moving rather quicker than C. -fwrapv implements it now.
Heater wrote:
Sat Jun 19, 2021 8:54 am
I wonder, does the C standard stimulate that endianness for ints has to be the same as endianness for floating point types?
That's a very good point. I discovered recently that you can test for them separately.
However, I find it very hard to believe they would differ. Easy to check at compile time!

Code: Select all

static_assert(__BYTE_ORDER__ == __FLOAT_WORD_ORDER__, "Integer and float byte orders differ!");
Heater wrote:
Sat Jun 19, 2021 8:54 am
Of course having to use magic, compiler specific switches like "-ffinite-math-only" brings us back to my claim that program behaviour cannot be determined from inspecting the source code. And it could change from machine to machine, compiler to compiler...
-ffinite-math-only is only an optimization. It removes checks that comparisons are "ordered" (have no NaN operands), and calls lightweight versions of the math library functions. Worthwhile if you know your code never deals with infinity or NaN's.

However, if you are going to use -ffinite-math-only, you might consider it prudent to check that all data is finite.
The compiler though, in its wisdom, removes calls to things like isnan() or isinf() or isfinite() because they will never return true :(
Hence the need for integer arithmetic to do the check.

Heater
Posts: 18335
Joined: Tue Jul 17, 2012 3:02 pm

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 11:35 am

jahboater wrote:
Sat Jun 19, 2021 9:16 am
Heater wrote:
Sat Jun 19, 2021 8:54 am
I'm not convinced about that. There was a proposal to make two's comp the standard representation in 2018: "N2218
Signed Integers are Two’s Complement": http://www.open-std.org/jtc1/sc22/wg14/ ... /n2218.htm
Interesting link.
Its more a case of "recognizing" or "acknowledging" that all known signed integer hardware is two's complement.
We always circle around to this subtle difference in perspective we have.

I am talking about what the C language actually defines and hence what one can deduce about a C program from reading it's source.

You, and many others it seems, are content with: All architectures, compiler implementations do something the same way, even if not specified in the standard, so everything is OK.





jahboater wrote:
Sat Jun 19, 2021 9:16 am
For C and C++ therefore, overflow is no longer UB. C++ is moving rather quicker than C. -fwrapv implements it now.
cppreference.com tells me :
undefined behavior - there are no restrictions on the behavior of the program. Examples of undefined behavior are data races, memory accesses outside of array bounds, signed integer overflow....
jahboater wrote:
Sat Jun 19, 2021 9:16 am
Heater wrote:
Sat Jun 19, 2021 8:54 am
Of course having to use magic, compiler specific switches like "-ffinite-math-only" brings us back to my claim that program behaviour cannot be determined from inspecting the source code. And it could change from machine to machine, compiler to compiler...
-ffinite-math-only is only an optimization. It removes checks that comparisons are "ordered" (have no NaN operands), and calls lightweight versions of the math library functions. Worthwhile if you know your code never deals with infinity or NaN's.

However, if you are going to use -ffinite-math-only, you might consider it prudent to check that all data is finite.
The compiler though, in its wisdom, removes calls to things like isnan() or isinf() or isfinite() because they will never return true :(
Hence the need for integer arithmetic to do the check.
If ones code behaves differently, or can behave differently in some situations, when a magic compiler switch is used, then clearly one cannot deduce the behaviour of the code from the source. Which is the thing that niggles me.

If ones code always behaves the same then of course there is no need for the magic switch.
Memory in C++ is a leaky abstraction .

User avatar
HermannSW
Posts: 4236
Joined: Fri Jul 22, 2016 9:09 pm
Location: Eberbach, Germany
Contact: Website Twitter YouTube

Re: TIL: "==" is not "is equal" for "double"

Sat Jun 19, 2021 1:44 pm

OK, in order to achieve xs:double equality for XML Schema fixed values, as defined by Schema spec
This makes the equality and ordering of values in the data space differ from that of [IEEE 754-1985] only in that for schema purposes NaN = NaN.

with a C implementation, this is the way how to implement "schema_eq(double, double)":

Code: Select all

int schema_eq(double d1, double d2)
{
  return  isnan(d1) ? isnan(d2) 
                    : d1 == d2;
}

According man page for isnan above does what is needed to "treat all C NaNs as equal":

Code: Select all

int isnan(x);
...
isnan(x)      returns a nonzero value if (fpclassify(x) == FP_NAN)
https://stamm-wilbrandt.de/2wheel_balancing_robot
https://stamm-wilbrandt.de/en#raspcatbot
https://github.com/Hermann-SW/Raspberry_v1_camera_global_external_shutter
https://github.com/Hermann-SW/raspiraw
https://stamm-wilbrandt.de/en/Raspberry_camera.html

Return to “C/C++”