5 ms·
I have certainly not had a bug that so plainly highlights this before. If it had been a bug about a more complex calculation than ( 4.0 * 0.1 ) / 0.1 I wouldn't
by MikeDucker 14y ago
I have certainly not had a bug that so plainly highlights this before. If it had been a bug about a more complex calculation than ( 4.0 * 0.1 ) / 0.1 I wouldn't have thought it worth mentioning. The fact that calc generally does result in a number that casts to 4 made it more interesting to me that in at least one case it didn't. I guess it was some state of the FPU (possibly) that gave a different result when there was sufficient time between the mult and the div. I will probably never know.
- acqq 14y agoThere's no "some state in the FPU" every partial result is repesented in 64 bits if you use doubles or in 32 bits if you use floats. Nothing is hidden "somewhere." It's especially nothing to "never know," every step can be followed exactly as long as you step through the assembly code or simply imagine that every partial result must also be represented as the FP number as specified not as you'd wish it to be. And don't think in decimal while you do the steps.
- MikeDucker 14y agoI remember being told a long time ago by our graphics programmer that the result of a floating point operation might be different depending on the state of the FPU. A quick internet search brings up things like this: http://www.codercorner.com/FPUFun.htm http://www.codercorner.com/FPUFun.htm which talks about setting the default rounding mode of the FPU. Not sure if that's what he was talking about, but it certainly bears no reference to my issue as I'm certainly not explicitly setting anything like that. That being that case can you give a reasonable explanation why carrying out ( x * y ) then later that function / y gave a value for x that casting would keep as x, when leaving the y division until sometime much later during the run resulted in a value of x - 1? There must have been something different between the two runs apart from literal time, but if nothing is hidden, then what is the difference that made the difference?
- acqq 14y agoAs far as I understand, you are not sure what your code does when. If you'd follow the steps of the code you wrote, you'd see that when the same values travel the same code paths they end up being always exactly the same. In programming the different results can come only from the different steps, unless you change the environment (which would be your intentional rounding mode change or if you read some uninitialized memory).
- MikeDucker 14y agoI would follow the steps again (I am genuinely interested in learning why it does what it does), but I ripped out the scaling factor a few days ago and reintroducing it in the hope that the same thing happens would be a lot of work, hence asking if there was some obvious reason that might be causing what I'm seeing. There must be some step I'm not appreciating or some state that's different. If something like this happens again I'll take a closer look into it.
- acqq 14y agoWhen you cross the line from the integers to the ten times smaller values (step 1) you move from the finite universe to the "approximate" one, unless the staring number ended with 0 or 5 and you divided with the exact constant 10 (even multiplying with .1 is not for all inputs the same as dividing with 10 because "exactly .1" doesn't exist in base 2 FP -- as another commenter said it's not the math you know from the school -- look at the bits). Once you are in the approximate world (it's in fact exact for itself but from your starting position not what you had or you think you have) you can exactly follow when you get the expected result and when you get what you haven't expected, but the real cause is always that in the step 1 you've lost an infinite(!) number of bits. If you don't want to worry, just don't do divisions with 10. Divide with 8 or 16 or 32...
- MikeDucker 14y agoI think that's excellent advice, which I'll take if I end up using box2D in the end :)
- scott_s 14y agoThat's not entirely accurate. See slide 1, bottom right: http://courses.cs.vt.edu/~cs3214/fall2011/gback/lectures/Lecture6-ia32-floatingpoint-advanced.pdf http://courses.cs.vt.edu/~cs3214/fall2011/gback/lectures/Lec... And: http://en.wikipedia.org/wiki/X87 http://en.wikipedia.org/wiki/X87 Basically, if you're using x87 for the floating point calculations, then it's possible for partial results to be 80-bits, and what values you observe can depend on when you write them back to memory (which will round them to 64-bits).
- acqq 14y agoHave you checked the code produced by your compiler and standard library? I don't know of any which uses 80 bits mode. Unless you turn on the 80 bits yourself, you don't use them. If you find such compiler please write! Another thing is that by the C standard, all calculations are performed on doubles, even when the result is to be stored as float. It's an ABC of C. :)
- emkemp 14y ago"all calculations are performed on doubles" is only true for traditional C. ANSI C permits single precision arithmetic if there are no double precision terms involved. Of course, C89 only provided standard math functions with double precision arguments, but C99 corrected that.
- acqq 14y agoYou are very correct, "permits." In practice the programmer must really be specific in the code, how he writes the constants (doubles are default) which functions he calls (doubles are default) and how he configures compiler (double intermediates were traditionally faster and defaults). There's a nice article on that topic: http://news.ycombinator.com/item?id=5138351 http://news.ycombinator.com/item?id=5138351 And it's just a part of the whole series of the nice FP articles.