#201 Float vs Decimal

jodastephen Sun 20 Apr 2008

The standard Float class, and thus the associated literal, appears to be a floating point number. I disagree with this.

The issues with floating point numbers are well known - they have very unexpected properties, because some numbers cannot be represented. Monetary values should never be represented in a Float for example.

Groovy has shown the way on this subject I would suggest. In Groovy, all decimal point literals refer to the Java BigDecimal class, and thus behave as most people would expect. Yes, performance is slower, but fan is already using objects instead of primitives, so that shouldn't be a concern.

Thus I would suggest:

Int i = 6;
Dec d = 2.34;
Float f = 2.34f;

Does this make sense?


brian Sun 20 Apr 2008

I definitely agree we need a Decimal class with a literal representation.

I'm not sure I'm ready to take the plunge to say 2.5 is a Decimal versus a Float without the suffix though. There is still a big difference in performance b/w BigDecimal and a native double and its seems like you should explicitly mark as 2.5d. In my industry we only use floating point (actually I have never found an occasion to use BigDecimal). But I'd like to hear other opinions.

Should it be sys::Dec or sys::Decimal? I kind of lean towards the longer name.

I'll add to Roadmap doc.

andy Sun 20 Apr 2008

That's interesting. Though I think I agree Float should be the default - so I vote:

  1. Using d for literals: 2.5d
  2. It should be sys::Decimal

jodastephen Sun 20 Apr 2008

I'm interested in finding out why you guys believe that Float should be the first class decimal, and Decimal the second class. Obviously the underlying implementations support float/double better than BigDecimal, but it that your main argument?

This perhaps gets at the heart of what you intend the language to be for. Is it a language for business or science?

If it is business, then Decimal should be preferred. I would argue that the vast majority of developers don't know how floating point numbers really work, and the tradeoffs involved - where 0.1 + 0.2 != 0.3. If they did, then you wouldn't see money classes written using floating point numbers.

If it is science (and I sense that your workplace is more science-led) then chance are that you know and understand the implications of floating point numbers.

Groovy chose Decimal, as they wanted to ensure that even business (non-technical) users could understand the numeric parts of the code.

This is a key decision in fan, simply because it helps imply who the language is for. If you want the language to be open to the masses (who won't all be as bright as you guys, and won't all understand floats) then making Decimal the default should be a natural choice.


andy Mon 21 Apr 2008

I don't think I've completely made up my mind, but keeping Floats simplifies interacting with native Java/C# libraries (though maybe not), and in practice, the accuracy issues have never really been a problem for me. So I don't want to take the performance hit unless I really care about things adding up exactly right.

brian Mon 21 Apr 2008

Our background is industrial control (I guess you call that science), and we use floating point extensively. But I agree there is a much wider audience for which decimal makes better sense, and that seems to be the trend in newer languages. BTW what is your background Stephen?

I'm not sure which type we pick for a non-suffixed literal makes too much difference. In the end everything is declared as a Float or Decimal, and Fan doesn't support auto-conversion (since a new object must be allocated). So I wouldn't characterize it as first class versus second class.

But I'd say right now I after some thought I lean towards no suffix meaning decimal too. It is a break from the C/Java/C# legacy, but it might make sense for Fan.

jodastephen Mon 21 Apr 2008

My background in in travel e-commerce, and as the co-spec lead of JSR-310 (Dates and Times). I will be at JavaOne speaking about JSR-310, so perhaps we can have a fan meetup session?

Unfortunately, I do think that which is without the suffix will make a huge difference. Sadly, most developers don't read the documentation, and will just go with the path of least resistance.

Those developers that need the speed of floating point are generally those that actually understand it, so its not unreasonable for them to have to add an f suffix.

brian Mon 21 Apr 2008

You are involved in Joda - duh didn't put that together with your username. That is cool stuff since the Java date APIs are such a mess.

Off the topic about decimals - having read your blog, I was interested in what you think about Fan's closures and how we did returns. I've avoiding allowing something like a break to be used within a closure to return control the enclosing method - but on occasion I really want it.

I also like your ideas about slot literals - that is something I implemented in another language I designed and it is quite handy.

Not sure if I can make JavaOne b/c my company has its own conference the week before. But if I can swing it, a meet-up would be great.

jodastephen Mon 21 Apr 2008

I do think that using return to return to the caller is soooooo much more intuitive for the Java/C# generation. If you want to have a long return, then I prefer the throw return syntax, as it is closest to how it will be implemented under the covers. I'd be interested in hearing your use cases for long returns (maybe a different thread?)

My email is scolebourne---joda+++org should you want to contact me :-)

brian Mon 21 Apr 2008

A little investigation - it doesn't look like the standard .NET libraries support a BigDecimal like class. So we need to give some thought to how we would actually implement this:

  • do we really need the overhead of infinite BigDecimal (the overhead looks really high - I counted ten 4byte fields + two objects in a single instance of BigDecimal)?
  • we might consider making sys::Decimal fixed point either 64-bit or 128-bit - that allows extremely fast computation and might handle most cases
  • try to do duplicate C# decimal's bit layout in Java (128-bit layout) - haven't found any good reference yet
  • googling around I saw some stuff for enhancing IEEE 754 with a standard decimal layout

Now I remember looking at this a couple years ago, and got stuck trying to figure out exactly how C# decimal worked under the covers.

jodastephen Mon 21 Apr 2008

I suspect that 128 bit might be good enough, at least for now. You could define 64 bit Java long for the integer component and a 64 bit Java long for the fractional part (which should cover 18dp).

brian Tue 22 Apr 2008

That is exactly what I was thinking - using two 64-bit longs.

helium Tue 22 Apr 2008

Perhaps this description of .Nets Decimal helps? (I just skimed through it) http://www.yoda.arachsys.com/csharp/decimal.html

brian Wed 23 Apr 2008

Great link helium - sounds like .NET decimal is decimal floating, not fixed point like I always assumed.

Since I think we are pretty much settled on a literal like 2.0 with no suffix being a Decimal, I've updated the compiler and serialization tokenizers to require the "f" or "F" suffix.

After sleeping on it, my current plan to tackle this project is to first implement Fan's Decimal class in Java via BigDecimal and in C# via the built-in decimal type. That will let us create a solid test suite against reference implementations. Then we can evaluate the VM variations and make a decision to tackle a custom implementation.

brian Thu 24 Apr 2008

This feature is mostly done:

  • Added sys::Decimal class
  • Decimal.java backed by BigDecimal (at least for now)
  • Compiler enhanced to support decimal literals
  • New decimal.def file in pod zip for literal table
  • Added LoadDecimal opcode for decimal literal
  • Enhanced JVM emit for new LoadDecimal opcode
  • Num.toDecimal (overridden by Int, Float)
  • Str.toDecimal
  • OutStream.writeDecimal (utf-8 string format)
  • InStream.readDecimal (utf-8 string format)
  • Update testCompiler and testSys
  • Update documentation

Tasks left:

  • Write C# Decimal class
  • Enhance .NET emit for LoadDecimal opcode
  • Enhance serialization to read/write decimal literals
  • Evaluate variations b/w BigDecimal and .NET decimal
  • Verify fanp disassembles correctly

andy Fri 25 Apr 2008

Implemented the .NET side. Two issues I came across:

  1. The range is smaller than BigDecimal: +/-1.0x10-28 to +/-7.9x10+28
  2. The default ToString() does not format the same as BigDecimal

If we're definitely going to use BigDecimal, I need to go back and implement a compatible toStr() method.

andy Fri 25 Apr 2008

Actually I tested the fromStr/toStr - and there doesn't appear to be any issues with Java reading .NET or vice versa. They just won't always look the same.

brian Sat 26 Apr 2008

This feature is complete. I'm not crazy about the portability of Decimal b/w Java and .NET, but I think this is good enough for now. From the updated Literal documentation:

NOTE: decimals don't operate exactly the same between the Java and .NET platform. Java uses BigDecimal which has an infinite range, while .NET uses System.Decimal with a range of of 28 significant digits and a range of 1E-28 to 7.9E+28. There is also a difference in equality between the two platforms:

3.00 == 3.0   =>  false on Java
3.00 == 3.0   =>  true on .NET
3.00 <=> 3.0  =>  zero on both platforms

Java treats trailing zeros as significant for equality, but they are insignificant on .NET. However both platforms produce consistent results for the Obj.compare method.

Login or Signup to reply.