Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issues obtaining empirical evidence to show difference in internal storage between float and double

Tags:

java

Someone asked this question yesterday. I understand that java interprets 3.0 to be a double, and 3f to be a float, and the doubles have 64-bit precision and floats have 32-bit precision. If someone asked me, I would have given an answer similar to the ones given; however, I wanted to provide empirical evidence that made it very clear that java stores 3.0 and 3f differently.

Here's what I attempted, but to little avail:

    System.out.println("Double: " + Double.toHexString(d)); //prints 0x1.8p1
    System.out.println("Float: " + Float.toHexString(f)); //prints 0x1.8p1

    System.out.println("Double: " + new Double(d).byteValue()); //prints 3
    System.out.println("Float: " + new Float(f).byteValue());  //prints 3 

    Long l = Double.doubleToLongBits(d);
    Integer i = Float.floatToIntBits(f);

    System.out.println("Double (long bits): " + l); //prints 4613937818241073152
    System.out.println("Float (int bits): " + i); //prints 1077936128

These are different, but that's because floats use single-format (an 8-bit biased exponent), and doubles use double-format (an 11-bit biased exponent). For more information, see IEEE Arithmetic. I converted both to their binary representations, but again didn't get anywhere:

    System.out.println("Long-toBinary: " + Long.toBinaryString(l)); 
    //prints 100000000001000000000000000000000000000000000000000000000000000
    System.out.println("Integer-toBinary: " + Integer.toBinaryString(i)); 
    //prints 1000000010000000000000000000000 

While there are more 0's in the Long.toBinaryString(l), multiplying by addition 0's will not change the result (the differing position of the 1 in the beginning is due to the 8-bit vs 11-bit biased exponent issue). So, I still don't have empirical evidence as to why the multiplication from the question below yields two different answers.

  97346822*3f, result is 2.9204048E8,

however

 97346822*3.0 gives me 2.92040466E8.

3f and 3.0 must be different for the results to be different, but I can not come up with anything showing that they actually are. Again, I understand that 3f is interpreted as a float and 3.0 as a double, please do not respond with answers merely stating this.

EDIT:
To clarify, I am trying to isolate 3.0 and 3f and show that they are indeed different.

like image 551
Steve P. Avatar asked Jul 26 '13 14:07

Steve P.


People also ask

Why would you use a float over a double?

float is mostly used in graphic libraries for high processing power due to its small range. double is mostly used for calculations in programming to eliminate errors when decimal values are being rounded off. Although float can still be used, it should only be in cases when we're dealing with small decimal values.

What happens when float and double is added?

To answer your question, you can add a float to a double and vice versa. Generally, the result will be made into a double , and you will have to cast it back to a float if that is what you want.

What is the difference between double and float variables in Java?

Size: Float is of size 32 bits while double is of size 64 bits. Hence, double can handle much bigger fractional numbers than float. They differ in the allocation of bits for the representation of the number. Both float and double use 1 bit for representing the sign of the number.


1 Answers

This answer also deals with this one.

3.0 and 3f themselves:

Their values are equal. We have some exponent and 2 consecutive ones in the value. This represents 3.0 and 3f equally. Now the problem is the floating-point multiplication. each floating point multiplication occurs on processor hardware, and all of the multiplication happens within a floating-point value itself, in the same precision(double/float). While the values 3.0 and 3f are equal, they are not equal in regards to how a multiplication occurs with them. This does not matter with strictfp on my system as my CPU matches the IEEE 754 standard precisely.

Other findings

This code could clue you in:

 BigDecimal val1=new BigDecimal(97346822*3f);
 BigDecimal val2=new BigDecimal(97346822*3.0);
 System.out.println(val1.subtract(val2, MathContext.UNLIMITED).toEngineeringString());
 System.out.println(val1.equals(val2));
 System.out.println(val1.compareTo(val2));

This creates two BigDecimals from our values.

First output is the difference as an engineering string, namely 14, not 0.

Second output is false showing that BigDecimal does not find them to be of equal value and precision.

Third output gives 1. Therefore value 1 is greater than value 2 in the context of BigDecimal.

Printing both precision( results shows they both have a precision of 9.

Different float/double values that can't be represented in binary without a repeating decimal

Let's run the same code with different values:

BigDecimal val1=new BigDecimal(3.1f);
BigDecimal val2=new BigDecimal(3.1);
System.out.println(val1.subtract(val2, MathContext.UNLIMITED).toEngineeringString()); //-95.367431729442841970012523233890533447265625E-9
System.out.println(val1.equals(val2)); //false
System.out.println(val1.compareTo(val2)); // -1
System.out.println(val1.precision()); // 22
System.out.println(val2.precision()); // 52

We can see that the same literal decimal, in float/double value, represents provably distinct values. In this case, the decimal 3.1 cannot be fit cleanly into a binary decimal. A similar effect happens with base 10 decimals and the fraction 2/3. While 2/3 would be written as:

  _
0.6

We'd write it with a limited length as 0.66666667. If we had a longer length, it would be something like 0.6666666666666667.

When taken literally, 0.66666667!=0.6666666666666667.

like image 60
nanofarad Avatar answered Sep 17 '22 10:09

nanofarad