2

I am not an expert in programming, and I am facing the following issue.

I need to compute modulo between floats A and B. So I use fmod((double)A, (double)B). Theorically, if A is a multiple of B, then the result is 0.0. However, due to floating point precision purpose, A and B are not exactly the number I expected to have. Then, the result of the modulo computation is not 0.0, but something different. Which is problematic.

Example: A=99999.9, but the compiler interprets it as 99999.898. B=99.9, but the compiler interprets it as 99.900002. fmod(A,B) expected to be 0.0, but gives actually 99.9.

So the question is: how do you use to manage this kind of situation ?

Thank you

LudoDu31
  • 31
  • 4

1 Answers1

2

The trouble is that:
A is not 99999.9, but 99999.8984375 and
B is not 99.9, but 99.90000152587890625 and
A mod B is 99.89691162109375

OP is getting the correct answer for the arguments given.

Need to use different augments.

A reasonable alternative is to convert the arguments by a scaled power-of-10, then round to an integer, %, back to floating point and un-scale.

Overflow is a concern.

Since OP wants to treat numbers to the nearest 0.1, scale by 10.

#include <float.h>
#include <stdio.h>

int main(void) {
  float A = 99999.9;
  float B = 99.9;
  printf("%.25f\n", A);
  printf("%.25f\n", B);
  printf("%.25f\n", fmod(A,B));
  long long a = lround(A*10.0);
  long long b = lround(B*10.0);
  long long m = a%b;
  double D = m/10.0;
  printf("D = %.25f\n", D);
  return 0;
}

Output

99999.8984375000000000000000000
99.9000015258789062500000000
99.8969116210937500000000000
D = 0.0000000000000000000000000

Alternative

  long long a = lround(A*10.0);
  long long b = lround(B*10.0);
  long long m = a%b;
  double D = m/10.0;

Scale, but skip the integer conversion part

  double a = round(A*10.0);
  double b = round(B*10.0);
  double m = fmod(a,b);
  double D = m/10.0;
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • The new decimal types in the next C Standard should provide an acceptable solution for this problem. – chqrlie Mar 24 '20 at 20:42
  • @chqrlieforyellowblockquotes Yes, looking forward to their use. But hold on to your hat when folks try to bit dibble that format. One thinks [binary64](https://en.wikipedia.org/wiki/Double-precision_floating-point_format). Try explaining decimal64 and friends. – chux - Reinstate Monica Mar 24 '20 at 20:45
  • @chqrlieforyellowblockquotes Even if it has to emulate (no hardware support)? – chux - Reinstate Monica Mar 24 '20 at 21:12
  • 1
    I don't know about gcc emulation, but [quickjs](https://bellard.org/quickjs/) has a software emulation for BigDecimal and BigFloat – chqrlie Mar 24 '20 at 21:29
  • 1
    @chqrlieforyellowblockquotes: No, they will not. **All** fixed-precision arithmetic has rounding errors: Integer, fixed-point, binary floating-point, decimal-floating-point. The reason people lose their heads over binary floating-point having some relatively tiny rounding error (e.g., in an operation is 7/6, it is around one part in nine quadrillion) but not over the ginormous errors in other arithmetic (in integer arithmetic, the error in 7/6 is one part in seven!) is because it is unfamiliar to them and hard to see using the decimal arithmetic they were taught in elementary school. – Eric Postpischil Mar 24 '20 at 23:48
  • 1
    @chqrlieforyellowblockquotes: So decimal floating-point will at least make the numbers easier for people to read and write, but the rounding errors will still be there in all operations except conversion from decimal (with not more digits) and to decimal (with not fewer digits). Divide by three, calculate a logarithm, evaluate an annual percentage rate over the period of a mortgage, and the errors will still be there. And once there is an error, neither `fmod` nor any other discontinuous function will give results as if real-number mathematics were used. – Eric Postpischil Mar 24 '20 at 23:50
  • 1
    @EricPostpischil: I agree 100%. Floating point math is not perfect and the use of Decimal types will not solve precision issues, ie: problems arising from computations that require rounding to represent in the destination type. The OP's problem will not be solved completely with them, but the classical problem where one must break down a sum into a minimum number of coins should no longer have surprising errors. – chqrlie Mar 25 '20 at 08:57