Why fmod (1.0.0.1) ==. 1?

I first experienced this phenomenon in Python, but it turned out that this is a general answer, for example MS Excel. Wolfram Alpha gives an interesting schizoid answer that says that a rational approximation of zero is 1/5. ( 1.0 mod 0.1 )

On the other hand, if I implement the definition manually, it gives me the "correct" answer (0).

def myFmod(a,n): return a - floor(a/n) * n 

What's going on here. Did I miss something?

+4
source share
6 answers

Because 0.1 not 0.1; this value does not appear to be double precision, so it is rounded to the nearest double precision number, which is exactly:

 0.1000000000000000055511151231257827021181583404541015625 

When you call fmod , you get the remainder of the division by the above value, which is equal to:

 0.0999999999999999500399638918679556809365749359130859375 

which is rounded to 0.1 (or possibly 0.09999999999999995 ) when printed. A.

In other words, fmod works fine, but you do not give it the input that you consider yourself to be.


Edit: Your own implementation gives the correct answer, because it is less accurate, believe it or not. First of all, note that fmod calculates the remainder without any rounding error; the only source of inaccuracy is a presentation error introduced using a value of 0.1 . Now let's look at your implementation and see how the rounding error that it carries exactly cancels the presentation error.

Rate a - floor(a/n) * n one step at a time, keeping track of the exact values ​​calculated at each step:

First we estimate 1.0/n , where n is the closest double precision approximation to 0.1 , as shown above. The result of this division is approximately equal to:

 9.999999999999999444888487687421760603063276150363492645647081359... 

Please note that this value is not a double precision representable number, therefore it is rounded. To see how this rounding occurs, let's look at a number in binary instead of decimal:

 1001.1111111111111111111111111111111111111111111111111 10110000000... 

The space indicates where rounding to double precision occurs. Since the part after the round point is larger than the exact half point, this value is rounded to an accuracy of 10 .

floor(10.0) is expected to be 10.0 . So all that remains is to calculate 1.0 - 10.0*0.1 .

In binary terms, the exact value is 10.0 * 0.1 :

 1.0000000000000000000000000000000000000000000000000000 0100 

again, this value does not appear to be double, and therefore is rounded at the position indicated by a space. This time it is rounded to the precision of 1.0 , so the final calculation is 1.0 - 1.0 , which of course is 0.0 .

There are two rounding errors in your implementation that exactly eliminate the error in representing the value 0.1 . fmod , by contrast, is always accurate (at least on platforms with a good number library) and produces a 0.1 representation error.

+22
source

This result is due to the floating point representation of the machine. In your method, you are 'casting' (kinda) float for int and don't have this problem. The "best" way to avoid such problems (esp for mod ) is to multiply by a sufficiently large set of ints (only 10 is required in your case) and perform the operation again.

FMOD (1.0,0.1)

fmod (10.0,1.0) = 0

+1
source

From man fmod :

The fmod () function computes the remainder of the floating point by dividing x by y. The return value is x - n * y, where n is the quotient of x / y, rounded to zero to an integer.

So what happens:

  • In fmod(1.0, 0.1) 0.1 is actually slightly larger than 0.1 because the value cannot be represented exactly as a float.
  • So n = x / y = 1.0 / 0.1000something = 9.9999something
  • When rounding to 0, n actually becomes 9
  • x - n * y = 1.0 - 9 * 0.1 = 0.1

Edit: As for why it works with floor(x/y) , as far as I can tell about it, it seems to be a FPU quirk. On x86, fmod uses the fprem , while x/y will use fdiv . Curiously 1.0/0.1 seems to accurately return 10.0 :

 >>> struct.pack('d', 1.0/0.1) == struct.pack('d', 10.0) True 

I believe fdiv uses a more accurate algorithm than fprem . Some discussion can be found here: http://www.rapideuphoria.com/cgi-bin/esearch.exu?thread=1&fromMonth=A&fromYear=8&toMonth=C&toYear=8&keywords=%22Remainder%22

+1
source

fmod returns xi * y, which is less than y, and I am an integer. 0.09 .... - due to floating point precision. try fmod(0.3, 0.1) -> 0.09... but fmod(0.4, 0.1) -> 0.0 , because 0.3 - 0.2999999 ... like a float.

fmod(1/(2.**n), 1/(2.**m) will never produce anything but 0.0 for the integer n> = m.

0
source

This gives the correct answer:

 a = 1.0 b = 0.1 a1,a2 = a.as_integer_ratio() b1,b2 = b.as_integer_ratio() div = float(a1*b2) / float(a2*b1) mod = a - b*div print mod # 0.0 

I think this works because it uses the rational equivalents of two floating point numbers that provide a more accurate answer.

0
source

The Python divmod function is instructive here. It tells you both the factor and the remainder of the division operation.

 $ python >>> 0.1 0.10000000000000001 >>> divmod(1.0, 0.1) (9.0, 0.09999999999999995) 

When you enter 0.1, the computer cannot represent this exact value in binary floating point arithmetic, so it selects the closest number that it can represent, 0.10000000000000001. Then, when you perform the division operation, floating point arithmetic decides that the factor should be 9, since 0.10000000000000001 * 10 is greater than 1.0. This leaves you with a balance that is slightly less than 0.1.

I want to use the new Python fractions module to get accurate answers.

 >>> from fractions import Fraction >>> Fraction(1, 1) % Fraction(1, 10) Fraction(0, 1) 

IOW, (1/1) mod (1/10) = (0/1) , which is equivalent to 1 mod 0.1 = 0 .

Another option is to implement a module operator so that you can specify your own policy.

 >>> x = 1.0 >>> y = 0.1 >>> x / y - math.floor(x / y) 0.0 
0
source

All Articles