• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
5
Question by hellcats · May 24, 2011 at 09:17 PM · floatingpoint

C# Floating Point Confusion

Consider this seemingly simple code:

 using UnityEngine;
 
 public class TestFloat : MonoBehaviour {
     private float x = 0.2f;
     private float y = 0.1f;
 
     public float SumFun() {
         return x + y;
     }
 
     void Update() {
         float t = SumFun();
         if (t != SumFun())
             Debug.Log("Wow");
     }
 }

The question is, why does it ever print anything? The C# standard in 11.1.5 says that floating point operations may be performed in higher precision than the result type of the operation, but in this case it shouldn't matter because both t and the result of SumFun() should convert to the same value in higher precision, and the result should still be equal.

It appears to me that the x+y in SumFun() is being computed at higher precision, and this higher precision result is being compared to t. But the problem with this is that SumFun() returns a float and not some higher precision value. Surely the compiler can't just change the semantics of the program like that? I'm just as surely confused.

UPDATE: I tried this program in Visual Studio 2008 on a PC and it doesn't print anything. So Unity is differing from .Net in this case.

UPDATE: For even more weirdness, change the comparison to:

 void Update() {
     float t = SumFun();
     if (t != (float)SumFun())
         Debug.Log("Wow");
 }

Casting the float result of SumFun() to a float makes it work!

UPDATE: I decided to summarize my thoughts about the ensuing discussion since I never got a clear answer as to why the code prints anything.

Most responders point out that floating-point is complicated, or at least more complicated than integer math. In particular I was warned away from relying on == (and !=) between floats. Reasons given include: 1) That certain decimal numbers aren't representable as floats (and therefore won't compare as equal. 0.1 being such a number) 2) Floating point arithmetic is therefore order dependent (e.g. 1.0 - 0.1 + 0.1 can give different results depending on the order it is evaluated). 3) Floating point arithmetic is rarely exact, even with high precision.

These are all true facts, but I don't think they really have anything to do with my code sample. If you changed all occurrences of "float" to "short", then the program would still be just as wrong, but no one would complain about doing == on shorts (note: I'm not claiming that the program would actually fail if you did that). My point being that == (or !=) between two float values is O.K. as long as the two float values are exactly the same number, and I claim that the above code should ensure that they are. The reason is that the return type of SumFun() is "float", which should cause any higher precision intermediate value to be truncated before being returned. At this point ‘t’ and the result of SumFun() should both be identical. And if you still think that you cannot compare identical floats with ==, then why does the the x86 include the FCOM instruction?

Now, I may be wrong about C#, and that is really what this question was about. But if in C# it is O.K. for the compiler to not perform the truncate to float when returning a value from SumFun(), then it would make the language very difficult to use. By the same logic you could also just ignore a cast to float, which would be absurd.

So, if Unity isn't actually compiling this code correctly then it is a bug.

But still, I'm sure my insistence that it is O.K. to use == with floats in certain circumstances won't sit well with some. I think it comes from being bitten by too many difficult to find bugs involving floating-point math. But another more subtle reason is that mathematics and floating-point on a computer are two different things, and it is easy to forget that. On a computer, a floating-point number is just a bit-pattern, whereas in mathematics it is an abstract idea. The IEEE standard describes how floating-point values are processed by a computer. The rules are complete and deterministic. Given two floats (a and b), the standard completely defines the result (c) of any given computation: c = a op b. a, b, and c are all just bit-patterns. 0.625 is 0x3F200000, and Pi is 0x40490FDA. I can add .625 to Pi and get 3.766593 which is 0x40710FDB. There is no reason I shouldn't then be able to compare the result to 3.766593, as long as I can ensure it is the same bit-pattern. Unfortunately, computer languages aren't always so precisely defined, and 11.1.5 of the C# specification seems to leave wiggle room for the compiler to do just about anything. However, 11.1.5 doesn't say that the result of a floating-point operation may be returned at higher precision, only that the computation may be performed at higher precision.

@macfanpro referred me to the excellent paper "What Every Computer Scientist Should Know About Floating Point Arithmetic" by David Goldberg. I was already familiar with this paper and think that everyone really should read it, but especially language designers and compiler writers :) Also, David Eberly is/was working on a book titled "Numerical Computing for Games and Science". I hope it gets published.

Comment
Add comment · Show 1
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Steel_Arm · Sep 20, 2014 at 07:52 AM 0
Share

I thought I would add to this for any who read. Casting to float explicitly forces the fp to be truncated. I'm no expert, but you can read this on Stack Overflow: http://stackoverflow.com/questions/14864238/coercing-floating-point-to-be-deter$$anonymous$$istic-in-net

8 Replies

· Add your reply
  • Sort: 
avatar image
4

Answer by ckfinite · May 24, 2011 at 09:36 PM

Well, the reason that you are having this problem is due to complicated problem surrounding the float type. For a complicated explanation, look at this: http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html

For a simple explanation: http://www.mikeash.com/pyblog/friday-qa-2011-01-04-practical-floating-point.html

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image hellcats · May 24, 2011 at 10:29 PM 0
Share

I looked at your first reference "What Every Computer Scientist Should $$anonymous$$now About Floating Point Arithmetic". The following comment is telling:

"Remarkably enough, some languages don't clearly specify that if x is a floating-point variable (with say a value of 3.0/10.0), then every occurrence of (say) 10.0*x must have the same value." ... "Thinking about floating-point in this fuzzy way stands in sharp contrast to the IEEE model, where the result of each floating-point operation is precisely defined".

I'm afraid that maybe C# is such a language :(

avatar image Bunny83 · May 24, 2011 at 10:55 PM 0
Share

The first link is really nice but i guess only 10% would even start reading it :D

avatar image ckfinite · May 25, 2011 at 07:37 PM 1
Share

C# actually uses the IEEE definition. Due to complicated problems outlined in the first link, the IEEE definition only requires the mathematical values in particular cases, e.g. x/x = 1 but x = 2, 2/x != 1.

avatar image hellcats · May 25, 2011 at 10:11 PM 0
Share

I understand that C# uses the IEEE standard for floating point computation, but that only describes the behavior of specific floating point instructions; it still leaves unspecified what instructions the compiler uses when translating a given piece of code. This is where the C# specification comes in. $$anonymous$$y point is that I really can't believe that it is legal C# for a function which returns a float to somehow ins$$anonymous$$d return a higher precision result. Yes, it can compute the higher precision result internally, but returning a float should be equivalent to putting a cast to float before the return.

This has nothing to do with the IEEE standard or floating-point representations. I could just as well have had a function which returns an short and the compiler ins$$anonymous$$d returned an int without truncation.

avatar image ckfinite · May 26, 2011 at 04:08 AM 0
Share

At this point, I really don't know. Floats have always been a bit weird for me.

avatar image
2

Answer by sneftel · May 24, 2011 at 09:39 PM

According to the C# standard, the compiler is free to perform floating-point computation at higher precision than requested, and that includes copying values around.

You should never rely on the result of a floating point calculation being exactly equal to any particular value, including some other value produced by an apparently identical computation. If you must check equality, use Mathf.Approximately() to improve robustness.

Comment
Add comment · Show 6 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image hellcats · May 24, 2011 at 09:50 PM 0
Share

Hmm... What a mess! I can't believe that you have to always use $$anonymous$$athf.Approximately to do floating-point comparisons. How ugly! How inefficient! I've never had such problems in C/C++.

avatar image sneftel · May 24, 2011 at 10:34 PM 4
Share

Then you haven't been using C/C++ very long -- it's subject to all the same implementation-defined behavior (and then some, because C/C++ compilers will often perform optimizations that reorder floating point ops). Not relying on floating-point equality is universally applicable advice, not specific to C#.

avatar image hellcats · May 24, 2011 at 10:41 PM 0
Share

But C++ at least defines sequence points that enforce order of operations. A function call is such a point. The addition of .1 and .2 in the function must be returned as float precision in C++, thus making the above program portable and predictable. I'm just trying to understand how to do the same sort of thing in C#.

avatar image Bunny83 · May 24, 2011 at 10:53 PM 0
Share

@Ben 12: exactly! I don't use C# very long but i guess it also do some optimisations. In C++ the function call would be completely removed since it returns a constant value (or to be more precise a constant equatation).

avatar image sneftel · May 24, 2011 at 10:58 PM 0
Share

C++ does not disallow reordering of operations over sequence points, only the reordering of side effect execution. The code you posted above, in C++, would NOT be portable, and it would not be predictable without changing compiler-specific settings.

Show more comments
avatar image
2

Answer by jspease · Feb 28, 2012 at 06:17 PM

I know this is coming late, but I saw an article that seems to really answer your question beyond the whole "floating point can be inexact stuff" hand-waving:

http://blogs.msdn.com/b/davidnotario/archive/2005/08/08/449092.aspx

"it’s still a language choice to give ‘predictability’ ... C#, for example, does nothing, if you want narrowing, you will have to insert (float) and (double) casts by hand."

This explains why t and SumFun() don't evaluate to equal: in your case, t probably got stored in memory whereas the second call to SumFun() got stored in a higher-precision register. This also explains why when you added an explicit (float) cast, you got the expected results. I find it a strange choice for C# to perform narrowing when casting to float but not when returning a float, but apparently it's "by design" (although admittedly I don't know which part of the C# standard makes it so).

So your workaround of casting the result to float is actually what you are supposed to have to do to get reliable results in this case, by design. You do not need to use Mathf.Approximately in cases where you expect an exact equality check to be a meaningful operation; you just have to be a little more careful and explicit about it in C# than in other languages.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by DaveA · May 24, 2011 at 09:36 PM

Floating point values are seldom exact, no matter the precision, which is why they have this: http://unity3d.com/support/documentation/ScriptReference/Mathf.Approximately.html

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
0

Answer by flaviusxvii · May 24, 2011 at 09:37 PM

This is fundamental computer science stuff. Some floating point numbers can't actually be represented in a 32 or 64 bit floating point value. It's always a matter of approximations.

http://unity3d.com/support/documentation/ScriptReference/Mathf.Epsilon.html http://unity3d.com/support/documentation/ScriptReference/Mathf.Approximately.html

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image hellcats · May 24, 2011 at 10:04 PM 0
Share

I understand that, and that is why I used .2 and .1 in the example above. These are both numbers that aren't exactly representable. But what I am trying to understand is how to deter$$anonymous$$istically compare such numbers without resorting to hacks such as $$anonymous$$athf.Approximately.

Whatever 32-bit representation C# uses should at least be comparable with itself. "if (.1f == .1f) ..." should work dang it!

avatar image flaviusxvii · May 24, 2011 at 10:32 PM 0
Share

C/C++ are compiled languages and so the compiler has more options for direct comparisons.

$$anonymous$$y advice is to DEAL WITH IT.

avatar image hellcats · May 24, 2011 at 10:35 PM 0
Share

Correct me if I'm wrong, but isn't C# also a compiled language? And I am trying to deal with it! That's why I asked the question!

avatar image flaviusxvii · May 24, 2011 at 10:44 PM 0
Share

Nope.. It's CLI interpreted.

avatar image yoyo · May 25, 2011 at 06:20 AM 0
Share

Usually. But on the iPhone C# is compiled ahead (though that doesn't really change the floating point precision issues).

  • 1
  • 2
  • ›

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

10 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

EulerAngles value as floating point 1 Answer

Gap Between Two terrain ... 2 Answers

floating point model using by unity 0 Answers

Why is this Int lagging when its subtracting something over a period of time? 1 Answer

Why do those two calculations return different values? 3 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges