Thijs van Dien 9 Posted January 2, 2021 (edited) (Cross posting from StackOverflow.) I'm facing the bizarre situation that the same program on the same machine doing a Round() of the same floating point value does not always give the same result. At first I thought it has to be glitch because of a bit flip or something, but it keeps on coming back. Now it started to happen on a completely different machine as well. Bad results come up rather rarely, in the order of once a week. When they do, it seems they keep happening until the program is restarted. That might be a coincidence, however. So far, I've been unable to reproduce it at will. This is all happening on the main thread, by the way. A check I consider adding is whether Round keeps pointing to the same memory address. Any other ideas? 2020-12-28 08:30:19.411 DBGrid1.Fields[17].AsString: 0,239999994635582 2020-12-28 08:30:19.411 FloatToStr(DBGrid1.Fields[17].AsFloat): 0,239999994635582 2020-12-28 08:30:19.411 FloatToStr(1000 * DBGrid1.Fields[17].AsFloat): 239,999994635582 2020-12-28 08:30:19.411 IntToStr(Round(1000 * DBGrid1.Fields[17].AsFloat)): 239 2020-12-28 08:30:19.411 FloatToStr(DBGrid1.Fields[17].AsSingle): 0,239999994635582 2020-12-28 08:30:19.411 FloatToStr(1000 * DBGrid1.Fields[17].AsSingle): 239,999994635582 2020-12-28 08:30:19.411 IntToStr(Round(1000 * DBGrid1.Fields[17].AsSingle)): 239 2020-12-28 08:30:19.411 FloatToStr(DBGrid1.Fields[17].AsExtended): 0,239999994635582 2020-12-28 08:30:19.412 FloatToStr(1000 * DBGrid1.Fields[17].AsExtended): 239,999994635582 2020-12-28 08:30:19.412 IntToStr(Round(1000 * DBGrid1.Fields[17].AsExtended)): 239 2020-12-28 08:30:19.412 CurrToStr(DBGrid1.Fields[17].AsCurrency): 0,2399 2020-12-28 08:30:19.412 CurrToStr(1000 * DBGrid1.Fields[17].AsCurrency): 239,9 2020-12-28 08:30:19.412 IntToStr(Round(1000 * DBGrid1.Fields[17].AsCurrency)): 239 2020-12-28 08:30:19.412 FloatToStr(Query1.FieldByName('FIELD').AsFloat): 0,239999994635582 2020-12-28 08:30:19.412 FloatToStr(1000 * Query1.FieldByName('FIELD').AsFloat): 239,999994635582 2020-12-28 08:30:19.412 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsFloat)): 239 2020-12-28 08:30:19.412 FloatToStr(Query1.FieldByName('FIELD').AsSingle): 0,239999994635582 2020-12-28 08:30:19.412 FloatToStr(1000 * Query1.FieldByName('FIELD').AsSingle): 239,999994635582 2020-12-28 08:30:19.413 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsSingle)): 239 2020-12-28 08:30:19.413 FloatToStr(Query1.FieldByName('FIELD').AsExtended): 0,239999994635582 2020-12-28 08:30:19.413 FloatToStr(1000 * Query1.FieldByName('FIELD').AsExtended): 239,999994635582 2020-12-28 08:30:19.413 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsExtended)): 239 2020-12-28 08:30:19.413 CurrToStr(Query1.FieldByName('FIELD').AsCurrency): 0,2399 2020-12-28 08:30:19.413 CurrToStr(1000 * Query1.FieldByName('FIELD').AsCurrency): 239,9 2020-12-28 08:30:19.413 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsCurrency)): 239 2020-12-28 08:30:19.413 CurrToStr(Query1.FieldByName('FIELD').AsCurrency): 0,2399 2020-12-28 08:30:19.413 CurrToStr(1000 * Query1.FieldByName('FIELD').AsCurrency): 239,9 2020-12-28 08:30:19.413 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsCurrency)): 239 2020-12-28 08:30:19.414 BinToHex(DBGrid1.Fields[17].AsFloat): 00000000008FC2F5FC3F 2020-12-28 08:40:46.461 DBGrid1.Fields[17].AsString: 0,239999994635582 2020-12-28 08:40:46.462 FloatToStr(DBGrid1.Fields[17].AsFloat): 0,239999994635582 2020-12-28 08:40:46.463 FloatToStr(1000 * DBGrid1.Fields[17].AsFloat): 239,999994635582 2020-12-28 08:40:46.463 IntToStr(Round(1000 * DBGrid1.Fields[17].AsFloat)): 240 2020-12-28 08:40:46.463 FloatToStr(DBGrid1.Fields[17].AsSingle): 0,239999994635582 2020-12-28 08:40:46.463 FloatToStr(1000 * DBGrid1.Fields[17].AsSingle): 239,999994635582 2020-12-28 08:40:46.463 IntToStr(Round(1000 * DBGrid1.Fields[17].AsSingle)): 240 2020-12-28 08:40:46.463 FloatToStr(DBGrid1.Fields[17].AsExtended): 0,239999994635582 2020-12-28 08:40:46.463 FloatToStr(1000 * DBGrid1.Fields[17].AsExtended): 239,999994635582 2020-12-28 08:40:46.463 IntToStr(Round(1000 * DBGrid1.Fields[17].AsExtended)): 240 2020-12-28 08:40:46.463 CurrToStr(DBGrid1.Fields[17].AsCurrency): 0,24 2020-12-28 08:40:46.463 CurrToStr(1000 * DBGrid1.Fields[17].AsCurrency): 240 2020-12-28 08:40:46.463 IntToStr(Round(1000 * DBGrid1.Fields[17].AsCurrency)): 240 2020-12-28 08:40:46.463 FloatToStr(Query1.FieldByName('FIELD').AsFloat): 0,239999994635582 2020-12-28 08:40:46.463 FloatToStr(1000 * Query1.FieldByName('FIELD').AsFloat): 239,999994635582 2020-12-28 08:40:46.463 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsFloat)): 240 2020-12-28 08:40:46.463 FloatToStr(Query1.FieldByName('FIELD').AsSingle): 0,239999994635582 2020-12-28 08:40:46.464 FloatToStr(1000 * Query1.FieldByName('FIELD').AsSingle): 239,999994635582 2020-12-28 08:40:46.464 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsSingle)): 240 2020-12-28 08:40:46.464 FloatToStr(Query1.FieldByName('FIELD').AsExtended): 0,239999994635582 2020-12-28 08:40:46.464 FloatToStr(1000 * Query1.FieldByName('FIELD').AsExtended): 239,999994635582 2020-12-28 08:40:46.464 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsExtended)): 240 2020-12-28 08:40:46.464 CurrToStr(Query1.FieldByName('FIELD').AsCurrency): 0,24 2020-12-28 08:40:46.464 CurrToStr(1000 * Query1.FieldByName('FIELD').AsCurrency): 240 2020-12-28 08:40:46.464 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsCurrency)): 240 2020-12-28 08:40:46.464 CurrToStr(Query1.FieldByName('FIELD').AsCurrency): 0,24 2020-12-28 08:40:46.464 CurrToStr(1000 * Query1.FieldByName('FIELD').AsCurrency): 240 2020-12-28 08:40:46.465 IntToStr(Round(1000 * Query1.FieldByName('FIELD').AsCurrency)): 240 2020-12-28 08:40:46.465 BinToHex(DBGrid1.Fields[17].AsFloat): 00000000008FC2F5FC3F Edited January 2, 2021 by Thijs van Dien Share this post Link to post
David Heffernan 2353 Posted January 2, 2021 Round is deterministic. Remember though that its input is its argument, and the floating point control state. Share this post Link to post
Thijs van Dien 9 Posted January 2, 2021 Check. I could extend the logging with a call to Get8087CW to see if that's the reason. If it is, how should I go about finding out how it ends up in the 'wrong' state? Share this post Link to post
Attila Kovacs 631 Posted January 2, 2021 Please pardon me for asking this, but are there ppl. using Delphi's built in Round()? Share this post Link to post
Thijs van Dien 9 Posted January 2, 2021 (edited) Me. 😉 This software has never experienced a problem like this before in over 20 years, so my first impulse is to track down what started causing this mess. I'm just not sure how if it I can't consistently reproduce it. Reading a little bit more about the topic, for example here, makes me rather hopeless about it all. I wonder what the sane approach would be if I want all my "own" rounding to behave like rmNearest. I could replace all calls to Round by a custom function, but that leaves a ton of libraries like FastReport potentially doing the wrong thing. Calling SetRoundMode in a timer is about as ugly as it gets... Edited January 2, 2021 by Thijs van Dien Share this post Link to post
David Heffernan 2353 Posted January 2, 2021 30 minutes ago, Thijs van Dien said: Check. I could extend the logging with a call to Get8087CW to see if that's the reason. If it is, how should I go about finding out how it ends up in the 'wrong' state? Could be all sorts of things. Calling almost any external module could change the control state. It's very hard to track it down. Share this post Link to post
Thijs van Dien 9 Posted January 2, 2021 (edited) What would your suggestion be to avoid this situation of (sudden) undesirable results in a big legacy application where I can't just go and change all the code? Edited January 2, 2021 by Thijs van Dien Share this post Link to post
David Heffernan 2353 Posted January 2, 2021 18 minutes ago, Thijs van Dien said: What would your suggestion be to avoid this situation of (sudden) undesirable results in a big legacy application where I can't just go and change all the code? There's no silver bullet here. You have to find every call that can potentially change the control state, and restore it when that call returns. Plus you've got the broken Delphi RTL which means that a change of control state in one thread can leak into another thread. It's worse on x64 than x86. I've discussed this here many times before. Emba know about the issues and have chosen not to address them. I've told them how to fix the issues in the RTL. In my code base I've replaced a number of RTL functions with thread safe versions, and added protection for whenever my code calls into external libraries. No silver bullet. Share this post Link to post
Thijs van Dien 9 Posted January 3, 2021 (edited) Alright, a library I recently started using more does indeed leave the rounding mode changed sometimes. It's a sad reality, but I'll be (even) more suspicious of third party code and try to make the software increasingly resilient on this front over time. For now, I hope removing a major cause will solve the issues experienced. Thanks! Edited January 3, 2021 by Thijs van Dien Share this post Link to post