<>
Archives Forums/BlitzMax Bug Reports/<>
| ||
<> strangeness...Local f:Double = 0.01 Local v1:Double = 2^f Local v2:Double = 2^f 'Print v1 ' uncomment any one of the commented lines and it works as expected 'Print v2 'If v1=v2 Print "~nis the same!" If v1<>v2 Print "~nis different?" ' should not be printed! 'If v1=v2 Print "~nis the same!" ' even uncommenting this one makes it work right! outputs... Building untitled2 Compiling:untitled2.bmx flat assembler version 1.67.25 (384728 kilobytes memory) 3 passes, 2375 bytes. Linking:untitled2.exe Executing:untitled2.exe is different? Process complete |
| ||
more strangeness...Local f:Double, n:Int Local a:Double, b:Double For n=0 To 100 a = 2^f ' change them to divide and it gives the same problem b = 2^f If a=b Print "same!~n" Else Print "a="+a+"~nb="+b+"~n" EndIf f:+0.01 Next only seems to happen if using power or divide in the math. (plus and minus works fine) |
| ||
Yes, it only "goes wrong" when you do anything that gets into the "Correct to X Decimal Place" |
| ||
I don't have Blitz here to test, but I would say there is a problem. If un-commenting the last line affects the outcome of the previous line, it's a problem, and although you're not supposed to compare floats for exact equality, if they are both the result of the exact same calculation they should compare absolutely equal. |
| ||
For me that code produces :Building untitled1 Compiling:untitled1.bmx flat assembler version 1.66 3 passes, 2375 bytes. Linking:untitled1.exe Executing:untitled1.exe Process complete We do have different levels of FASM though. |
| ||
if they are both the result of the exact same calculation they should compare absolutely equal. You shouldn't even count on that. What happens is that float and double are 32-bit and 64-bit values. But calculations are done in 80-bit registers. Sometimes these values must be stored to memory, in 32 or 64 bit precision, which slightly changes their value. So even though the calculations look the same in source code the resulting values might be a little different depending on whether certain registers were available. A command such as Print, which also has to call a function to convert numbers to strings, may need to temporarily use some registers. It does look odd that this can affect a calculation before the call to Print. But I suppose the compiler is simplifying things by dealing with blocks of code when deciding what values can stay in registers. |
| ||
@tonyg, Just tried it with FASM 1.66 and I get the same result as with 1.67.25 @Floyd, I see your point that values might change if stored in an extended register or in memory, but IMHO that is kind of assuming that one of the variables has been poped out of a register and the other one is still there. Local f:Double, n:Int Local a:Double, b:Double Local errors:Int For n=0 To 100 a = 2^f b = 2^f If a<>b errors:+1 f:+0.01 Next Print errors In the above code print isnt used until after the loop block, and I would expect all (if any) variables to stay in registers. (I admit I know nothing about asm, so I might be way off, just my opinion) Might well be a FASM problem and out of Marks hands though. *EDIT* Just tried the same thing in C++ and it works as expected. |
| ||
The version of FASM shouldn't matter. I have BlitzMax 1.28 and don't see any errors in the three examples given above. But I have seen this sort of thing in past. One particularly strange effect of keeping some values in registers while doing repeated store/load operations on others is that single precision values may be too accurate. |
| ||
Hi, There is indeed a weird bug in there...investigating. |
| ||
Hi, Ok, not a compiler bug - well, the compiler's doing what it should anyway - but a 64 vs 80 bit issue. The '^' operator does in fact involve a function call, so the result of 'a=2^f' is first stored to 64 bit (as it's a double) memory. This is so it's not wiped out by the function call in the subsequent 'b=2^f'. However, the result of 'b=2^f' is NOT being stored to memory - no need, as there are no function calls between it's definition and it's use. So 'b' is being 'stored' with 80 bit precision - thus the mismatch. In a way, I guess this is partially the optimizers fault! Note this only happens in release mode. In debug mode, all locals are stored to memory, so are all 'forced' to 64 bit (although I can think of how to break it in debug mode too). Not really sure what the fix is for this. Currently, float returning functions all really return 80 bit values (since they are returned in registers), so one fix could be to force float functions to 'chop' returned values by doing a store/load before returning them. However, built-in operators like +, * etc that don't require function calls would also really need to be chopped in some (which?) cases. Another approach would be to 'chop' comparison parameters. Or, all temps could be stored 80 bit - although I don't think this would work due to hidden 'guard bits' in the FPU. All of which would result in slower code - I think I'll need to do a bit more research on this! |
| ||
Another solution would be to introduce two new comparitors. "Equal Enough", and "Quite different" |
| ||
Floyd, My apologies mate, you're spot on! I'll add you to my "clever buggers not to argue with" list :) Mark, do you think its worth fixing at all, chances of this cropping up in real apps must be pretty small? I only found it because I was checking C++ functions against ones I had prototyped in Max. |
| ||
Hi, "Equal Enough", and "Quite different" This is actually really hard to do, as it all depends on how 'different' the parameters are. I've seen some pretty complex and not so complex ways of doing it, but none I'd consider general enough for inclusion into the language. Mark, do you think its worth fixing at all, chances of this cropping up in real apps must be pretty small? Well, it crops up all the time really! I probably don't notice it myself as I learned long ago never to use = or <> with floats unless I *know for sure* they're holding ints or 1/power-of-two values. But none-the-less, c/c++ do appear to do better it better than Max so I'll look into it one of these days. |
| ||
It seems to me that as the value you can store in ram is 64 bit, then the "true" value is the 64 bit value, and so that is what you should compare in a comparison operation. But when in the process of doing math operations, I think you should do them with the greatest speed and precision possible, so you should not chop the values when doing that. That said, it is my own personal practice never to try to compare two floats to see if they are equal, because of precision issues like this. I always check to see if they're less than/equal to or greater than/equal to some value, and never count on even decrementing 10 by .1 in a loop to ever result in a precise value like 0. |
| ||
Yeah, I agree, its not the sort of thing I'd do normaly either. Dirty solution... How about a "definitely equals" where any 80bit values are moved first, so no overhead for normal comparisons. ( == or something ) |