ArduPilot / ArduPilot/ardupilot
`feenableexcept` and `fedisableexcept` don't seem to work
- Dominant language
- C++
- Stars
- 15.9k
- Forks
- 21.4k
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 119
Description
## Bug report
**Issue details**
`feenableexcept` and `fedisableexcept` in [fenv.h](https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_Common/missing/fenv.h) don't seem to work.
I'm not an ardupilot user, I found this by accident almost an year ago while doing something on flang. The test file was lost in my disk and I found it by accident today.
run_fenv_tests.c
```
#include
#include
#include
/* gcc -Wno-overflow fenv_tests.c -o fenv_tests.out -lm */
/* clang -Wno-constant-conversion -Wno-overflow fenv_tests.c -o fenv_tests.out -lm */
/* The ardupilot version is from https://github.com/ArduPilot/ardupilot/blob/master/libraries/AP_Common/missing/fenv.h */
int ardupilot_feenableexcept(unsigned int excepts)
{
#pragma FENV_ACCESS ON // GNU
// #pragma STDC FENV_ACCESS ON // Apple
fexcept_t flags;
/* Save current exception flags. */
fegetexceptflag(&flags, FE_ALL_EXCEPT);
feclearexcept(FE_ALL_EXCEPT); /* clear all fp exception conditions */
return fesetexceptflag(&flags, excepts) != 0 ? -1 : flags; /* set new flags */
}
int ardupilot_fedisableexcept(unsigned int excepts)
{
#pragma FENV_ACCESS ON // GNU
// #pragma STDC FENV_ACCESS ON // Apple
fexcept_t flags;
/* Save current exception flags. */
fegetexceptflag(&flags, FE_ALL_EXCEPT);
feclearexcept(FE_ALL_EXCEPT); /* clear all fp exception conditions */
return fesetexceptflag(&flags, ~excepts) != 0 ? -1 : flags; /* set new flags */
}
int
__fenv_feenableexcept(int exc)
{
fexcept_t new_flagp, old_flagp;
int new_exc, old_exc;
// the subset of exc that's already on
old_exc = fetestexcept(exc);
// the subset of exc that's not already on
new_exc = old_exc ^ exc;
// get the states of the excepts we need to turn on
fegetexceptflag(&old_flagp, new_exc);
// flips all flags
new_flagp = ~old_flagp;
// sets all new_exc flags to the state in new_flagp (on)
fesetexceptflag(&new_flagp, new_exc);
return 0;
}
int
__fenv_fedisableexcept(int exc)
{
fexcept_t new_flagp, old_flagp;
int new_exc, old_exc;
// the subset of exc that's on
old_exc = fetestexcept(exc);
// the subset of exc we need to turn off
new_exc = old_exc & exc;
// get the states of the excepts we need to turn off
fegetexceptflag(&old_flagp, new_exc);
// flips all flags
new_flagp = ~old_flagp;
// sets all new_exc flags to the state in new_flagp (off)
fesetexceptflag(&new_flagp, new_exc);
return 0;
}
int
__fenv_fegetexcept(void)
{
return fetestexcept(FE_ALL_EXCEPT);
}
void print_bits(unsigned short v) {
/* shamelessly copied from https://codereview.stackexchange.com/questions/231901/printing-the-bits-of-an-integer-using-bitfields-and-union */
unsigned short mask = -1u; /* 11111... */
mask -= (unsigned short)(mask / 2); /* 10000... */
while (mask) {
printf("%d ", (v & mask) != 0);
mask >>= 1;
}
// printf("\n");
}
void test_my_except(char* name, fexcept_t exc) {
int initial, final;
initial = __fenv_fegetexcept();
print_bits(initial);
printf("-> Current register value");
printf("\n");
__fenv_feenableexcept(exc);
// ardupilot_feenableexcept(exc);
print_bits(__fenv_fegetexcept());
printf("-> %s is enabled", name);
printf("\n");
__fenv_fedisableexcept(exc);
// ardupilot_fedisableexcept(exc);
final = __fenv_fegetexcept();
print_bits(final);
printf("-> %s is disabled\n", name);
if (initial == final)
printf("%s was tested successfully", name);
else {
printf("%s test FAILED!", name);
exit(1);
}
printf("\n\n");
}
void test_ardupilot_except(char* name, fexcept_t exc) {
int initial, final;
initial = __fenv_fegetexcept();
print_bits(initial);
printf("-> Current register value");
printf("\n");
ardupilot_feenableexcept(exc);
print_bits(__fenv_fegetexcept());
printf("-> %s is enabled", name);
printf("\n");
ardupilot_fedisableexcept(exc);
final = __fenv_fegetexcept();
print_bits(final);
printf("-> %s is disabled\n", name);
if (initial == final)
printf("%s was tested successfully", name);
else {
printf("%s test FAILED!", name);
exit(1);
}
printf("\n\n");
}
int main() {
printf("============== MY FUNCTIONS ==============\n");
test_my_except("FE_INEXACT", FE_INEXACT);
test_my_except("FE_DIVBYZERO", FE_DIVBYZERO);
test_my_except("FE_UNDERFLOW", FE_UNDERFLOW);
test_my_except("FE_OVERFLOW", FE_OVERFLOW);
test_my_except("FE_INVALID", FE_INVALID);
// test_my_except("FE_FLUSHTOZERO", FE_FLUSHTOZERO); /* only on apple platforms */
test_my_except("FE_ALL_EXCECPT", FE_ALL_EXCEPT);
printf("============== ARDUPILOT FUNCTIONS ==============\n");
test_ardupilot_except("FE_INEXACT", FE_INEXACT);
test_ardupilot_except("FE_DIVBYZERO", FE_DIVBYZERO);
test_ardupilot_except("FE_UNDERFLOW", FE_UNDERFLOW);
test_ardupilot_except("FE_OVERFLOW", FE_OVERFLOW);
test_ardupilot_except("FE_INVALID", FE_INVALID);
// test_my_except("FE_FLUSHTOZERO", FE_FLUSHTOZERO); /* only on apple platforms */
test_ardupilot_except("FE_ALL_EXCECPT", FE_ALL_EXCEPT);
return 0;
}
```
Compile with `gcc run_fenv_tests.c -o run_fenv_tests -lm`.
On the output you will see that the ardupilot's `feenableexcept` and `fedisableexcept` never set any bits in the control register.
Example output on linux x86_64
```
============== MY FUNCTIONS ==============
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 -> FE_INEXACT is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_INEXACT is disabled
FE_INEXACT was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 -> FE_DIVBYZERO is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_DIVBYZERO is disabled
FE_DIVBYZERO was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 -> FE_UNDERFLOW is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_UNDERFLOW is disabled
FE_UNDERFLOW was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 -> FE_OVERFLOW is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_OVERFLOW is disabled
FE_OVERFLOW was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 -> FE_INVALID is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_INVALID is disabled
FE_INVALID was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 -> FE_ALL_EXCECPT is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_ALL_EXCECPT is disabled
FE_ALL_EXCECPT was tested successfully
============== ARDUPILOT FUNCTIONS ==============
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_INEXACT is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_INEXACT is disabled
FE_INEXACT was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_DIVBYZERO is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_DIVBYZERO is disabled
FE_DIVBYZERO was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_UNDERFLOW is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_UNDERFLOW is disabled
FE_UNDERFLOW was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_OVERFLOW is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_OVERFLOW is disabled
FE_OVERFLOW was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_INVALID is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_INVALID is disabled
FE_INVALID was tested successfully
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> Current register value
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_ALL_EXCECPT is enabled
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -> FE_ALL_EXCECPT is disabled
FE_ALL_EXCECPT was tested successfully
```
This seems like a bug to me. If anyone can confirm I will gladly open a pull request.
**Version**
Any
**Platform**
[X] All
[ ] AntennaTracker
[ ] Copter
[ ] Plane
[ ] Rover
[ ] Submarine
**Airframe type**
NA
**Hardware type**
NA
**Logs**
NA
Contributor guide
Research direction
Start with libraries/AP_Common/missing/fenv.h and reproduce the report using the supplied run_fenv_tests.c command on Linux x86_64. Compare the ArduPilot functions with the observed control-register output and verify that enabling and disabling each listed exception changes the register as expected, then returns it to its initial state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100