gnembon / fabric-carpet

Fabric Carpet
MIT License
1.72k stars 275 forks source link

[Scarpet] More problems with - 0 #1374

Open Ghoulboy78 opened 2 years ago

Ghoulboy78 commented 2 years ago

I seem to recall we had a problem with -0 which broke a bunch of things when we were using % operator. I had a thorough solution but it was scrapped in favour of a situational fix in the % operator. I don't wanna be the guy to say I told you so, but I did mention that this problem would resurface. This time, it's a problem in division. Doing (1- 4/5) - 0.2 should result in 0, but instead gives -0. It also appears when doing (1 - 9/10) - 0.1. The problem doesn't arise when I do (1 - 3/5) - 0.4, which results in 0. I think this might be an issue of the optimizer? but I can't tell. I'm still in favour of a proper solution which doesn't allow to initialize a NumericValue with -0. It's not even that complicated and solves this problem entirely.

Links to #843 The solution I'm proposing is the one rejected in #845

CommandLeo commented 2 years ago

It has happened to me a few times to have the result of a query of an entity's motion be -0 (at least it happened when formatting with str() and using %f).

17183248569 commented 2 years ago

maybe number(str(1-4/5-0.2)) ? that will turn them into normal 0

Ghoulboy78 commented 2 years ago

true. But that's too much computation for what should be a simple operation

altrisi commented 2 years ago

To what point should this be an issue? -0 is a valid floating point number according to IEEE 754, and that's what Java (and many hardware according to wikipedia) follows.

IMO we should try to identify where does it cause issues and whether the behaviour of those operations should be modified instead to account for this value. Trying to completely get rid of it is either complicating all operations that can result in it or adding an extra runtime cost to all number calculations. As a user you should be able to get rid of -0.0 by adding 0.0 to it if you really need a positive one.

Relates to #843.

Ghoulboy78 commented 2 years ago

You're right that we shouldn't add excessive unnecessary compute, but my fix in #845 adds like 1 conditional which checks whether the value is -0, and if so makes it 0. But still I see how it's best to avoid this extra runtime cost.

But there are problems which arise when getting -0, and while in this case the behaviour could (I think) be fixed by fixing division operator, I'm not sure that we should be playing a game of whack-a-mole with this. But given that's what gnembon essentially said last time, I guess that may be the solution he wants to adopt.

altrisi commented 2 years ago

My issue really is, -0 is a thing. Not really in math, but it is in computing. Should scarpet go against many other languages and those standards? I'm not really against the change, I also find -0 strange, mostly wondering about that. There must be a reason it exists in the first place, people wouldn't have made a standard with -0 that has had multiple revisions and kept it for almost 40 years for nothing (I hope they wouldn't at least).

Ghoulboy78 commented 2 years ago

https://softwareengineering.stackexchange.com/questions/280648/why-is-negative-zero-important This appears to give a pretty decent reason for why we should have -0. Something about complex numbers, and potential uses in machine learning. The machine learning uses actually are more relevant to scarpet now that we have bit-modifying functions etc. So ig there is a relatively good reason to keep -0, and also if we add complex numbers it may help in handling them. I won't lie, I didn't quite fully read through what was being said.

But long story short, I am now of the opinion that we should continue to play whack-a-mole with -0, not get rid of it entirely. So I'mma submit a pr which fixes the division operator.

Ghoulboy78 commented 2 years ago

Ok it turns out the problem is not fixed by just fixing division operator.

17183248569 commented 2 years ago

Do not dig floating point thing. java support for addition, subtraction, multiplication and division is reliable, just use it. -0 exists for a reason. it is a valid float number.

Ghoulboy78 commented 2 years ago

Well if we are to accept that -0 is going to exist, then we would need to add a section in the docs to explain it to people who may not understand.

Well I have found the root of this problem. It appears that when I do 1 - 0.8, it does not exactly compute as 0.2 (cos of floating point precision errors). This means that while 1- 4/5 is displayed as 0.2 to the screen (like if you do print(1-0.8)), it is considered as a value just smaller than 0.2. So then when I subtract 0.2 from it, it gives me a number just smaller than 0, and that means that it gives -0. The only perplexing thing is why it only appears when it is in the form:

(1 - (n-1)/n) - 1/n

N can be an integer or a float, it doesn't seem to matter. I then came up with this code to check how many numbers are affected by this:

negative_zero(n)->((1-(n-1)/n) - 1/n); l = []; loop(100000, if(negative_zero(_+1)<0, l+= _+1)); print(l); length(l)

Between 0 and 100000, there appear to be 50365 numbers affected. I think it's because of how those numbers (and their reciprocals) are represented in the floating point standard, so roughly half of all numbers are affected.

Ghoulboy78 commented 2 years ago

After more thought, I have decided not to fix this issue. I will leave this issue open until gnembon decides whether or not to fix it or leave it, but I have now been swayed (largely by myself), as I feel that trying to get rid of this issue is like trying to whack an entire field of moles.