2

I use mod() to compare if a number's 0.01 digit is 2 or not.

if mod(5.02*100, 10) == 2 
...
end

The result is mod(5.02*100, 10) = 2 returns 0;

However, if I use mod(1.02*100, 10) = 2 or mod(20.02*100, 10) = 2, it returns 1.

The result of mod(5.02*100, 10) - 2 is

ans =

  -5.6843e-14

Could it be possible that this is a bug for matlab?

The version I used is R2013a. version 8.1.0

Mike
  • 1,841
  • 2
  • 18
  • 34
  • 3
    I think this is an instance of [floating point error](http://floating-point-gui.de/) – Blorgbeard Mar 20 '14 at 00:00
  • 2
    To add to the previous comment: the floating point representation of 5.02*100 is not exactly equal to that of 502. See [here](http://stackoverflow.com/questions/686439/why-is-24-0000-not-equal-to-24-0000-in-matlab) – Luis Mendo Mar 20 '14 at 00:06
  • But the question is why 1.02*100 is 102. Any other number is correct except for 5.02. It's weird – Mike Mar 20 '14 at 00:32
  • Not any other number. Try `4.02 * 100`. Some numbers can be exactly represented by a float, and some cannot. – Blorgbeard Mar 20 '14 at 01:54
  • Anyway, to "fix" it, you need to round the result of `5.02*100` to the nearest integer before you check its `mod`. I don't speak matlab, but I'm sure there's an easy way to do that. – Blorgbeard Mar 20 '14 at 01:56
  • I think you want to use `==` (comparison) instead of `=` (assignment) in these code samples. – Andrew Janke Mar 20 '14 at 01:58
  • @AndrewJanke, thank you very much! yes, I corrected. – Mike Mar 20 '14 at 13:21
  • @Blorgbeard, yes, I can do that. I actually work around the issue, But I'm curious at the reaons. :-) – Mike Mar 20 '14 at 13:22

2 Answers2

4

To use mod() correctly to test the particular digit of a number, use round() to round it to the nearest whole number and compensate for floating point error.

mod(round(5.02*100), 10) == 2

What you're encountering is a floating point error or artifact, like the commenters say. This is not a Matlab bug; it's just how floating point values work. You'd get the same results in C or Java. Floating point values are "approximate" types, so exact equality comparisons using == without some rounding or tolerance are prone to error.

>> isequal(1.02*100, 102)
ans =
     1
>> isequal(5.02*100, 502)
ans =
     0

It's not the case that 5.02 is the only number this happens for; several around 0 are affected. Here's an example that picks out several of them.

x = 1.02:1000.02;
ix = mod(x .* 100, 10) ~= 2;
disp(x(ix))

To understand the details of what's going on here (and in many other situations you'll encounter working with floats), have a read through the Wikipedia entry for "floating point", or my favorite article on it, "What Every Computer Scientist Should Know About Floating-Point Arithmetic". (That title is hyperbole; this article goes deep and I don't understand half of it. But it's a great resource.) This stuff is particularly relevant to Matlab because Matlab does everything in floating point by default.

Andrew Janke
  • 23,508
  • 5
  • 56
  • 85
4

This is not a bug in MATLAB. It is a limitation of floating point arithmetic and conversion between binary and decimal numbers. Even a simple decimal number such as 0.1 has cannot be exactly represented as a binary floating point number with finite precision.

Computer floating point arithmetic is typically not exact. Although we are used to dealing with numbers in decimal format (base10), computers store and process numbers in binary format (base2). The IEEE standard for double precision floating point representation (see http://en.wikipedia.org/wiki/Double-precision_floating-point_format, what MATLAB uses) specifies the use of 64 bits to represent a binary number. 1 bit is used for the sign, 52 bits are used for the mantissa (the actual digits of the number), and 11 bits are used for the exponent and its sign (which specifies where the decimal place goes).

When you enter a number into MATLAB, it is immediately converted to binary representation for all manipulations and arithmetic and then converted back to decimal for display and output.

Here's what happens in your example:

Convert to binary (keeping only up to 52 digits):

5.02 => 1.01000001010001111010111000010100011110101110000101e2  
100  => 1.1001e6  
10   => 1.01e3  
2    => 1.0e1

Perform multiplication:

    1.01000001010001111010111000010100011110101110000101    e2
  x 1.1001                                                  e6
--------------------------------------------------------------
    0.000101000001010001111010111000010100011110101110000101
    0.101000001010001111010111000010100011110101110000101
  + 1.01000001010001111010111000010100011110101110000101
-------------------------------------------------------------
    1.111101011111111111111111111111111111111111111111111101e8

Cutting off at 52 digits gives 1.111101011111111111111111111111111111111111111111111e8
Note that this is not the same as 1.11110110e8 which would be 502.

Perform modulo operation: (there may actually be additional error here depending on what algorithm is used within the mod() function)
mod( 1.111101011111111111111111111111111111111111111111111e8, 1.01e3) = 1.111111111111111111111111111111111111111111100000000e0

The error is exactly -2-44 which is -5.6843x10-14. The conversion between decimal and binary and the rounding due to finite precision have caused a small error. In some cases, you get lucky and rounding errors cancel out and you might still get the 'right' answer which is why you got what you expect for mod(1.02*100, 10), but In general, you cannot rely on this.

Doug Lipinski
  • 644
  • 4
  • 9