When to use float (in teaching)

S

Stefan Ram

I teach some properties of the data type »double«:

public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( 0.1 + 0.1 + 0.1 == 0.3 );
java.lang.System.out.println( 1.1 * 1.1 == 1.21 ); }}

false
false

. I also teach that beginners should not use the data type
»float«, because it will only cause trouble.

But now a student came up with this:

public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( 0.1f + 0.1f + 0.1f == 0.3f );
java.lang.System.out.println( 1.1f * 1.1f == 1.21f ); }}

true
true

. So, now it looks as if double just does the wrong thing
and as if float just does the right thing.

How does one deal with this, when explaining, when to use
»float« and when to use »double«?
 
M

Mark Thornton

Stefan said:
I teach some properties of the data type »double«:

public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( 0.1 + 0.1 + 0.1 == 0.3 );
java.lang.System.out.println( 1.1 * 1.1 == 1.21 ); }}

false
false

. I also teach that beginners should not use the data type
»float«, because it will only cause trouble.

But now a student came up with this:

public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( 0.1f + 0.1f + 0.1f == 0.3f );
java.lang.System.out.println( 1.1f * 1.1f == 1.21f ); }}

true
true

. So, now it looks as if double just does the wrong thing
and as if float just does the right thing.

In both cases float and double are working as should be expected. Just
because floating point types often do not represent decimal values
exactly doesn't mean that some values are not represented exactly. In
the case of float the result of 0.1f+0.1f+0.1f is not exactly equal to
0.3 (because 0.3 is not exactly representable by either float or
double). It just happens the nearest float to 0.3 (which is what 0.3f
is) is equal to the result of 0.1f+0.1f+0.1f.

There is a valuable place for the use of float and double in scientific
and engineering computation. Statistics is another area where the use of
floating point is hard to avoid. Outside of these areas they should
mostly be avoided. Unfortunately BigInteger and BigDecimal are bit
tedious to use due to the absence of operator overloading for these types.


Mark Thornton
 
L

Lew

  I teach some properties of the data type »double«:

public class Main
{ public static void main( final java.lang.String[] args )
  { java.lang.System.out.println( 0.1 + 0.1 + 0.1 == 0.3  );
    java.lang.System.out.println(       1.1 * 1.1 == 1.21 ); }}

false
false

  . I also teach that beginners should not use the data type
  »float«, because it will only cause trouble.

  But now a student came up with this:

public class Main
{ public static void main( final java.lang.String[] args )
  { java.lang.System.out.println( 0.1f + 0.1f + 0.1f == 0.3f  );
    java.lang.System.out.println(        1.1f * 1.1f == 1..21f ); }}

true
true

  . So, now it looks as if double just does the wrong thing
  and as if float just does the right thing.

That is a completely deceptive appearance. You just happened to pick
examples that gave that illusion. In reality, both float and double
are doing the right thing, and you cannot count on either one to yield
an exact comparison using '==' with values that are not exactly
representable in finite-precision binary. In reality float has higher
error, because in either case expected error of a term will start at 1
ulp.
  How does one deal with this, when explaining, when to use
  »float« and when to use »double«?

One uses double when high precision is required, which is most of the
time. One uses float when high precision is not so necessary, as,
say, Swing does in calculating screen positions, and when speed is
more important than precision.
 
M

Mark Thornton

Lew said:
One uses double when high precision is required, which is most of the
time. One uses float when high precision is not so necessary, as,
say, Swing does in calculating screen positions, and when speed is
more important than precision.

Float is used when memory is short, you need a very large number of
values and can tolerate the lower precision. The performance advantage
is modest (and often more a result of fitting more values in the
processor cache than increased speed of the basic operation).
Any exception to this is with GPUs where float is fast but double either
slow or non existent. However this doesn't (yet) apply directly to Java.

Mark Thornton
 
L

Lew

Christian said:
Also a situtation where float is preferred for me to double:
A volatile float is written atomic  [sic] while a volatile double is not!

That is totally not true.

Volatile doubles are written atomically. JLS 17.7:
 
M

Mark Thornton

Lew said:
Christian said:
Also a situtation where float is preferred for me to double:
A volatile float is written atomic [sic] while a volatile double is not!

That is totally not true.

Volatile doubles are written atomically. JLS 17.7:
Writes and reads of volatile long and double values are always atomic.

It was true of some early JVMs.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4023233

Note that despite what it says in that bug report, this has been fixed.

Mark Thornton
 
L

Lew

Christian said:
Also a situtation where float is preferred for me to double:
A volatile float is written atomic  [sic] while a volatile double is not!
That is totally not true.
Volatile doubles are written atomically.  JLS 17.7:

Mark said:
It was true of some early JVMs.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4023233

Note that despite what it says in that bug report, this has been fixed.

That is so 20th century!

Note that the memory model changed significantly (with Java 5) also,
in particular with respect to the semantics of 'volatile'.

I think it's safe to challenge on the basis of the JLS an incorrect
statement about Java semantics irrespective of bugs that were fixed
years and years ago. The point is that Sun's JVM and presumably every
other one extant today conforms to the language specification
regarding atomic reads and writes of volatile longs and doubles.

As Java developers we have to go by the specification, not by bugs in
its implementations. Those that rely on violations of the
specification risk having their code break when the bug is fixed, as,
say, Apache commons-lang did in tbeir "enum" implementation prior to
Java 5 that broke when Sun fixed the bug (and Apache refused to fix
it). Java programmers have a right to expect Java implementations to
conform to the specification.

That is moot here, anyway, since volatile actually works as specified
in this matter: reads and writes of volatile longs and doubles are
atomic. To state otherwise does a huge disservice to those who might
not know better.
 
L

Lew

Those that rely on violations of the
specification risk having their code break when the bug is fixed, as,
say, Apache commons-lang did in tbeir "enum" implementation prior to
Java 5 that broke when Sun fixed the bug (and Apache refused to fix
it).  Java programmers have a right to expect Java implementations to
conform to the specification.

I should clarify: the Apache commons-lang "enum" bug had nothing to do
with 'volatile'. It was that certain methods in their "enum" relied
on references to a 'class' literal causing initialization of the
owning class. When Sun fixed that initialization bug, those Apache
commons "enum" methods broke.
 
M

Mark Thornton

Lew said:
Christian said:
Also a situtation where float is preferred for me to double:
A volatile float is written atomic [sic] while a volatile double is not!
That is totally not true.
Volatile doubles are written atomically. JLS 17.7:
Writes and reads of volatile long and double values are always atomic.

Mark said:
It was true of some early JVMs.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4023233

Note that despite what it says in that bug report, this has been fixed.

That is so 20th century!

Note that the memory model changed significantly (with Java 5) also,
in particular with respect to the semantics of 'volatile'.

I think it's safe to challenge on the basis of the JLS an incorrect
statement about Java semantics irrespective of bugs that were fixed
years and years ago. The point is that Sun's JVM and presumably every
other one extant today conforms to the language specification
regarding atomic reads and writes of volatile longs and doubles.

Nevertheless, it can be important to check our assumptions. Long ago I
did check for atomic assignment found that it didn't work. Just a few
days ago I found some code that depended on the behaviour of
Timestamp.getTime() as exhibited by JVMs up to 1.4. In 1.4, Sun changed
the behaviour and left the documentation very confusing (especially for
those aware of the previous behaviour).

In this case I was just adding an historical note that there was some
basis for believing the assignments not to be atomic, but today it is
about as valid as suggesting that Java is slow!

Mark Thornton
 
T

Tom Anderson

. I also teach that beginners should not use the data type
?float?, because it will only cause trouble.

Interesting. FWIW, i use doubles when i'm doing mathematics, and floats
when i'm doing sums - which is an aphoristic way of saying that i use
doubles when i'm doing calculations where i care about accuracy, range,
etc, whcih is things like numerical work, data processing, whatever, and
floats when i'm doing things like calculating a load factor for a
hashtable, or the index of the 95th centile in an array, etc.

tom
 
S

Stefan Ram

Thomas Pornin said:
The usual rule is the following: compute with the most accurate
type you have (double) but store only as many bits as are
significant in your data

This sounds good.

However, when float is used to store data, a rank beginner
might be confused with situations, where »3.7 is not 3.7
anymore«:

public class Main
{ public static void main( final java.lang.String[] args )
{ final float a = 3.7f;
java.lang.System.out.println( a == 3.7 ); }}

false

And a rank beginner usually does not need to store very
many floating point values.

Therefore, I deem that in my classes, where I teach to rank
beginners¹ and have very little time, it is appropriate to
recommend not to use float at all. When any of those students
continues to learn Java after the end of my classes he will
eventually learn about when to use »float«.

1) In an introductory class about C++ with about 10 students,
I asked some years ago, who of them has ever heard the term
»objektorientierte Programmierung« (= »object-oriented
programming«). No hand was raised.
 
S

Stefan Ram

However, when float is used to store data, a rank beginner
might be confused with situations, where »3.7 is not 3.7
anymore«:

»... confused by situations where ...«
And a rank beginner usually does not need to store very
many floating point values.

Another point in teaching is that many beginners tend to
believe that »float« should be used as the standard floating
point type, just because it is /named/ »float«, which is akin
to »floating point«.
 
L

Lew

Mark said:
Nevertheless, it can be important to check our assumptions. Long ago I
did check for atomic assignment found that it didn't work. Just a few
days ago I found some code that depended on the behaviour of
Timestamp.getTime() as exhibited by JVMs up to 1.4. In 1.4, Sun changed
the behaviour and left the documentation very confusing (especially for
those aware of the previous behaviour).

All right, I'll bite. What was the previous behavior, and how did it change
in Java 1.4?
 
J

Joshua Cranmer

Thomas said:
It shall be noted that on some architectures, including x86 CPU in
32-bit mode, floating-point computations are performed with an
internal 80-bit format(**), regardless of whether operands were
initially floats or doubles.

Doesn't strictfp require limiting the computation to be purely 32- or
64-bit?
"Within an FP-strict expression, all intermediate values must be
elements of the float value set or the double value set" (JLS 3 §15.4)
 
N

Nigel Wade

Lew wrote:

One uses double when high precision is required, which is most of the
time. One uses float when high precision is not so necessary, as,
say, Swing does in calculating screen positions, and when speed is
more important than precision.

Or if memory requirements dictate that the overhead of 64bit rather than 32bit
data is significant. For most programming applications these days this won't be
an issue. However, if you have to process very large arrays of real numbers it
could become significant, or are working on a system with very restricted
memory. There may also be cases where it's necessary/desirable to reduce
accuracy down to float before performing I/O over restricted bandwidth
transports, but still perform all the internal arithmetic as double.

The days when double was a luxury to be avoided unless essential have thankfully
passed. I don't see any reason not to use double as the default, and only use
float if necessary.
 
N

Nigel Wade

Christian said:
Stefan said:
I teach some properties of the data type »double«:

public class Main
{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( 0.1 + 0.1 + 0.1 == 0.3 );
java.lang.System.out.println( 1.1 * 1.1 == 1.21 ); }}

false
false

. I also teach that beginners should not use the data type
»float«, because it will only cause trouble.

imho this is a bad thing to tell...
Its like telling "Don't use int because it will only give you trouble
, always use long"

This is a false comparison. Short, int and long all have identical accuracy, the
only difference is in their range. If the data you need to process can be held
in an int you might as well use an int, there is nothing whatever to be gained
by using long. This is most definitely not the case when dealing with floating
point numbers.
For most applications (as in 99% of all I see) float is more than enough
for floating point computations.

But why use float at all? Unless there is a compelling reason to do so (speed,
memory limitations) why not just use double as the default? There is no good
reason not to. Starting off by learning to use float gets you into the habit of
using float and may have serious repercussions at some later date.
And as one usually uses int and it
doesn't matter if you use long instead ... in some applications you are
just better off with using int or short or byte where applicable ...


There are 2 situations where double really starts to be useful ...
1. very high numbers ... and you don't want to use BigInt and co...

Or very small numbers
2. high numbers that need high precicision ... if the number is in the
Billions and you still need precicion behind the comma ...

any number that requires higher precision, it's not a function of the magnitude
unless it exceeds the exponent limits of float.
2. sometimes happens i.e. if very large numbers are multiplied with very
small ones.. like a * a^-1
1. is rather rare...

or when calculating the difference between a large and small number etc.

In fact, you might as well just use double everywhere, and only use float when
it's required...
 
M

Mark Thornton

Lew said:
All right, I'll bite. What was the previous behavior, and how did it
change in Java 1.4?

Prior to 1.4 Timestamp.getTime() always returned a multiple of 1000;
that is an exact second. The fraction was then obtained from the
getNanoseconds method. From 1.4 the getTime() method returns
nanoseconds/1000000. The value returned by getNanoseconds hasn't
changed, so code that relied on the pre 1.4 behaviour tends to get an
overall time where the fractional seconds are counted twice.

I suspect this change resulted from an effort to reduce the problems
arising from mixing java.util.Date with java.sql.Timestamp (notably
comparisons didn't work properly).

Mark Thornton
 
L

Lew

Prior to 1.4 Timestamp.getTime() always returned a multiple of 1000;
that is an exact second. The fraction was then obtained from the
getNanoseconds method. From 1.4 the getTime() method returns
nanoseconds/1000000. The value returned by getNanoseconds hasn't
changed, so code that relied on the pre 1.4 behaviour tends to get an
overall time where the fractional seconds are counted twice.

I suspect this change resulted from an effort to reduce the problems
arising from mixing java.util.Date with java.sql.Timestamp (notably
comparisons didn't work properly).

Looking at the Javadocs for the Java 6 version of java.sql.Timestamp
Note: This type is a composite of a java.util.Date and a separate nanoseconds value.
Only integral seconds are stored in the java.util.Date component.

Since the change you describe for java.sql.Timestamp is a change in
specification, then my earlier arguments against reliance on bugs do
not apply to this case. Relying on an obsolete specification is not a
programmer's right. It's like relying on pre-Java 5 code that has a
variable or method named 'enum' and being angry that it violates Java
5's rules.

Apache commons-lang relied on a class-initialization bug that violated
the JLS extant at the time they relied on it, and when the bug was
fixed, Apache refused to correct their mistake. That's a horse of a
different color.

Also, since this is an API class, not part of the Java language
itself, and not mentioned in the JLS, my arguments about reliance on
the JLS don't apply to this case. Javadocs are generated from the
implementation of an API, and therefore are the product of an
interpretation of the specification for that API, not the
specification itself. There can be, and clearly sometimes are, errors
in the implementation of Javadocs just as in the Java source itself.

I am claiming that Java programmers have a right to rely on the JLS
for the version of Java we're using. The JLS is the rigorous,
normative description of Java's rules. I am also claiming that
violating those rules just because a bug happens to let you do so is
extremely bad practice.
 
R

Roedy Green

{ public static void main( final java.lang.String[] args )
{ java.lang.System.out.println( 0.1f + 0.1f + 0.1f == 0.3f );
java.lang.System.out.println( 1.1f * 1.1f == 1.21f ); }}

Floats are for measuring desk sizes. It does not matter if the desk
is 143.99998 cm or 143.0 cm or 143.0002. You can't tell such desks
apart for any practical purpose. If you start treating floats like
currency you are in trouble. They are not SUPPOSED to add up to the
penny, just get the approximate answer over a wide range of
magnitudes.

See http://mindprod.com/jgloss/floatingpoint.html


Perhaps an analogy fretting over the inability of floats to add up
perfectly would be like judging Britney Spears for ability to do
carpentry. That is not what she is for. It is utterly preposterous to
hire her to remodel your bathroom, so judging her on that ability
makes no sense.

--
Roedy Green Canadian Mind Products
http://mindprod.com

"Species evolve exactly as if they were adapting as best they could to a changing world, and not at all as if they were moving toward a set goal."
~ George Gaylord Simpson
 
T

Tom Anderson

What is the advantage of float for the cases where you do use it?

Performance, of course! :)

There's no great advantage. For me, this works as a form of documentation
- whenever i see a float, i know it's something sums-ish rather than
mathematical.

tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top