I asked today if wrong answers should be flagged
and I cited an example with IMHO wrong answers.
Now Robert Harvey deleted my answer on the very same question:
If the user wants to "compare two floats using a set number of decimal points (significant figures)" there is a simple but unfortunate answer:
It is NOT possible. All other answers which say it is or provide algorithms are WRONG (naturally with the assumption that operations like + - * and / should provide correct decimal answers.
Reason: The decision to round either upward or downward depends if the next digit is either 0.49999999999999 or 0.5000000001. As floats are always incorrect approximations to both values, you simply cant, either we would not have the need to use decimal or money classes.
The C# documentation even cites an example: http://msdn.microsoft.com/en-us/library/75ks3aby.aspx
Notes to Callers Because of the loss of precision that can result from representing decimal values as floating-point numbers or performing arithmetic operations on floating-point values, in some cases the Round(Double, Int32) method may not appear to round midpoint values to the nearest even value in the digits decimal position. This is illustrated in the following example, where 2.135 is rounded to 2.13 instead of 2.14. This occurs because internally the method multiplies value by 10digits, and the multiplication operation in this case suffers from a loss of precision.
And as float has only 24 bits, it means that 16 777 216+1 == 16777216. You are screwing up if you use floats for decimal points.
EDIT: Because it seems that noone wants to see the problem, here the proof:
The very first problem is that the algorithms here are broken: AlmostEquals(0.06f, 0.14f, 1) = true.
The reason is that the original poster asked explicitly for equal decimal places. But even if we use a special round function, there is no way to escape the problem.
I used VC 2008 to print out the correct values of the Math.pow function. The first is the precision parameter, the second the hex value of the resulting float and the third is the exact decimal value.
1 3dcccccd 0.100000001490116119384765625
2 3c23d70a 0.00999999977648258209228515625
3 3a83126f 0.001000000047497451305389404296875
4 38d1b717 0.0000999999974737875163555145263671875
5 3727c5ac 0.00000999999974737875163555145263671875
6 358637bd 9.999999974752427078783512115478515625E-7
So you can try
AlmostEquals(0.0f, 0.100000001490116119384765625f, 1) or
AlmostEquals(0.0f, 0.0999999940395355224609375, 1)
and show me a function which is able to get correct answers for both values and is generalizable.
I see here a little problem.
May I ask why ?
Even if you assume that my answer is NOT correct....
There is an answer: It is NO. Due to floating-point restrictions it is not possible to write a function which is able to work exactly as the author intended. You may get a function which works sometimes
I added the Microsoft documentation of the round function which explicitly warns that the result will sometimes not work as expected.
I provided a counterexample of both suggested implementations AlmostEquals(0.06f, 0.14f, 1) = true.
I also printed out the results of the VC2008 implementation to show that the stepsizes do not fulfill the necessary conditions and show that it is in fact not possible to generate a function which satify both tests and can be generalized.
EDIT: It was mentioned in the comments that the question is vague. If this is the case, then surely it is not the problem of my answer, it applies to the question and the answers given.
But I cite now the OP:
@MrLister My original problem was that I wanted (0.1F + 0.2F) == 0.3F to be true (which returns false in C#). But the implementation can be used to say that 123.123 almost equals 123.124 with 2 decimal places. I'm not concerned that 12,300 would equal 12,400 if you took 2 significant figures into account. That's what I meant about decimal places
@sixlettervariables: Actually upon further reading, it's decimal-places I'm trying to get at here. Not significant figures.
If you find the question vague, tell me an alternative implementation for which "123.123 almost equals 123.124 with 2 decimal places." AND which wants decimal places, not significant figures.
EDIT2: Added the deleted answer because some people cannot see it.