29

I have an exam question I am revising for and the question is for 4 marks.

"In java we can assign a int to a double or a float". Will this ever lose information and why?

I have put that because ints are normally of fixed length or size - the precision for storing data is finite, where storing information in floating point can be infinite, essentially we lose information because of this

Now I am a little sketchy as to whether or not I am hitting the right areas here. I very sure it will lose precision but I can't exactly put my finger on why. Can I get some help, please?

pjs
  • 18,696
  • 4
  • 27
  • 56
stan
  • 433
  • 1
  • 5
  • 7
  • 3
    Where do you get the idea that floats are infinite? – Paul Tomblin May 06 '10 at 12:39
  • well the precision they have can be modified cant it? – stan May 06 '10 at 12:47
  • 7
    Ofcourse `float` and `double` do not have infinite precision. If that were the case, they would be magical things that could store an infinite amount of information in a finite amount of memory space. – Jesper May 06 '10 at 12:55
  • 1
    No, it can't. At least not for the floats that you have in Java. For arbitrary precision arithmetic, you have the BigDecimal class, of course. – Michael Borgwardt May 06 '10 at 12:55
  • 1
    Storing an `int` into a `float` which did not previously hold anything useful and then discarding or overwriting the `int` could lose information, but it would be the act of discarding or overwriting the `int` that caused the information loss--not the storing of the `float`. Conversely, overwriting any variable of any type that held the only copy of something useful could cause information loss, even if the variable was made to store a perfect copy of the information in some other variable. – supercat Sep 04 '13 at 07:44

9 Answers9

59

In Java Integer uses 32 bits to represent its value.

In Java a FLOAT uses a 23 bit mantissa, so integers greater than 2^23 will have their least significant bits truncated. For example 33554435 (or 0x200003) will be truncated to around 33554432 +/- 4

In Java a DOUBLE uses a 52 bit mantissa, so will be able to represent a 32bit integer without lost of data.

See also "Floating Point" on wikipedia

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
Dead account
  • 19,587
  • 13
  • 52
  • 82
  • 12
    The mantissa is actually 24 and 53 bits for float and double, respectively. It's just that the highest bit is not stored in the representation, because it's not needed (it is always 1). – slacker May 08 '10 at 22:13
28

It's not necessary to know the internal layout of floating-point numbers. All you need is the pigeonhole principle and the knowledge that int and float are the same size.

  • int is a 32-bit type, for which every bit pattern represents a distinct integer, so there are 2^32 int values.
  • float is a 32-bit type, so it has at most 2^32 distinct values.
  • Some floats represent non-integers, so there are fewer than 2^32 float values that represent integers.
  • Therefore, different int values will be converted to the same float (=loss of precision).

Similar reasoning can be used with long and double.

Julien Kronegg
  • 4,968
  • 1
  • 47
  • 60
dan04
  • 87,747
  • 23
  • 163
  • 198
21

Here's what JLS has to say about the matter (in a non-technical discussion).

JLS 5.1.2 Widening primitive conversion

The following 19 specific conversions on primitive types are called the widening primitive conversions:

  • int to long, float, or double
  • (rest omitted)

Conversion of an int or a long value to float, or of a long value to double, may result in loss of precision -- that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value, using IEEE 754 round-to-nearest mode.

Despite the fact that loss of precision may occur, widening conversions among primitive types never result in a run-time exception.

Here is an example of a widening conversion that loses precision:

class Test {
         public static void main(String[] args) {
                int big = 1234567890;
                float approx = big;
                System.out.println(big - (int)approx);
        }
}

which prints:

-46

thus indicating that information was lost during the conversion from type int to type float because values of type float are not precise to nine significant digits.

polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
15

No, float and double are fixed-length too - they just use their bits differently. Read more about how exactly they work in the Floating-Poing Guide .

Basically, you cannot lose precision when assigning an int to a double, because double has 52 bits of precision, which is enough to hold all int values. But float only has 23 bits of precision, so it cannot exactly represent all int values that are larger than about 2^23.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • 3
    Rather than saying it can't hold `int` values larger than 2^23, it would be more precise to say that the range of consecutive representable integers extends from precisely -2^24 to +2^24. Those values and all values between them are representable in `float`; the nearest integers above below that range are not. – supercat Nov 26 '13 at 21:20
8

Your intuition is correct, you MAY loose precision when converting int to float. However it not as simple as presented in most other answers.

In Java a FLOAT uses a 23 bit mantissa, so integers greater than 2^23 will have their least significant bits truncated. (from a post on this page)

Not true.
Example: here is an integer that is greater than 2^23 that converts to a float with no loss:

int i = 33_554_430 * 64; // is greater than 2^23 (and also greater than 2^24); i = 2_147_483_520
float f = i;
System.out.println("result: " + (i - (int) f)); // Prints: result: 0
System.out.println("with i:" + i + ",  f:" + f);//Prints: with i:2_147_483_520,  f:2.14748352E9

Therefore, it is not true that integers greater than 2^23 will have their least significant bits truncated.

The best explanation I found is here:
A float in Java is 32-bit and is represented by:
sign * mantissa * 2^exponent
sign * (0 to 33_554_431) * 2^(-125 to +127)
Source: http://www.ibm.com/developerworks/java/library/j-math2/index.html

Why is this an issue?
It leaves the impression that you can determine whether there is a loss of precision from int to float just by looking at how large the int is.
I have especially seen Java exam questions where one is asked whether a large int would convert to a float with no loss.

Also, sometimes people tend to think that there will be loss of precision from int to float:
when an int is larger than: 1_234_567_890 not true (see counter-example above)
when an int is larger than: 2 exponent 23 (equals: 8_388_608) not true
when an int is larger than: 2 exponent 24 (equals: 16_777_216) not true

Conclusion
Conversions from sufficiently large ints to floats MAY lose precision.
It is not possible to determine whether there will be loss just by looking at how large the int is (i.e. without trying to go deeper into the actual float representation).

Julien Kronegg
  • 4,968
  • 1
  • 47
  • 60
Sinkrad
  • 91
  • 1
  • 2
  • 7
    All integers in the range -2^24 to +2^24 are representable as `float`, as are all even integers -2^25 to +2^25, all multiples of four -2^26 to +2^26, etc. To quickly see if an int will convert losslessly, divide by 20,000,000, take the smallest power of two that's at least that big, divide by that, and if the result exceeds 16,777,216 divide by two again. If the power-of-two division or final divide-by-two yields a fraction, the number is not representable. – supercat Jun 20 '14 at 16:47
  • odd numbers bigger than 2^24 always loss precision. Or not divided by 4 numbers greater than 2^25, and so on... – Djeefther Souza Apr 18 '17 at 18:09
4

Possibly the clearest explanation I've seen: http://www.ibm.com/developerworks/java/library/j-math2/index.html the ULP or unit of least precision defines the precision available between any two float values. As these values increase the available precision decreases. For example: between 1.0 and 2.0 inclusive there are 8,388,609 floats, between 1,000,000 and 1,000,001 there are 17. At 10,000,000 the ULP is 1.0, so above this value you soon have multiple integeral values mapping to each available float, hence the loss of precision.

1

There are two reasons that assigning an int to a double or a float might lose precision:

  • There are certain numbers that just can't be represented as a double/float, so they end up approximated
  • Large integer numbers may contain too much precision in the lease-significant digits
Adam Batkin
  • 51,711
  • 9
  • 123
  • 115
  • 1
    only your second point is relevant to int values. all mathematical integers can be represented precisely with a binary floating point format of sufficient length. the (64bit) java double format has sufficient length for all (32bit) java int values, but the (32bit) java float format does not, which results in rounding of least-significant digits. your first point is only relevant to fractional numbers, not whole numbers. – james turner Jul 10 '15 at 14:57
0

For these examples, I'm using Java.

Use a function like this to check for loss of precision when casting from int to float

static boolean checkPrecisionLossToFloat(int val)
{
  if(val < 0)
  {
    val = -val;
  }
  // 8 is the bit-width of the exponent for single-precision
  return Integer.numberOfLeadingZeros(val) + Integer.numberOfTrailingZeros(val) < 8;
}

Use a function like this to check for loss of precision when casting from long to double

static boolean checkPrecisionLossToDouble(long val)
{
  if(val < 0)
  {
    val = -val;
  }
  // 11 is the bit-width for the exponent in double-precision
  return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 11;
}

Use a function like this to check for loss of precision when casting from long to float

static boolean checkPrecisionLossToFloat(long val)
{
  if(val < 0)
  {
    val = -val;
  }
  // 8 + 32
  return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 40;
}

For each of these functions, returning true means that casting that integral value to the floating point value will result in a loss of precision.

Casting to float will lose precision if the integral value has more than 24 significant bits.

Casting to double will lose precision if the integral value has more than 53 significant bits.

HesNotTheStig
  • 549
  • 1
  • 4
  • 9
-3

You can assign double as int without losing precision.

SSSSS1
  • 1
  • 1