15

I want to understand why the POSITIVE_INFINITY and NEGATIVE_INFINITY constants are defined only for floating-point data types (float, double and their wrappers),

public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;

but not for integral data types (byte, short, int, long and their wrappers). This affects the divide operation result on different data types. For example:

For integral types:

int z = 10/0;
System.out.println(z);

Output:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at TesterClass.main(TesterClass.java:16)

For floating-point types:

double z = 10/0.0;
System.out.println(z);

Output:
Infinity
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Monil Panchal
  • 328
  • 2
  • 12
  • 5
    From the language spec: “The floating-point types are float and double, which are conceptually associated with the single-precision 32-bit and double-precision 64-bit format IEEE 754 values and operations as specified in IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Standard 754-1985 (IEEE, New York).” Why the IEEE standard states that, I would expect it to be a long story. – Ole V.V. Dec 24 '16 at 10:44
  • 3
    "This affects the divide operation result on different data types" - no it doesn't, they're just a convenient way to get certain floating point values. There are no corresponding integer values so there is nothing they could be defined as. – harold Dec 24 '16 at 10:57
  • 4
    See also http://stackoverflow.com/questions/12954193/why-does-division-by-zero-with-floating-point-or-double-precision-numbers-not/12954420#12954420 – Raedwald Dec 24 '16 at 11:23
  • 1
    I have used a dozen programming langauges, and I have never heard of a language that included special values for infinity and/or not-a-number for integral types. Apparently the need hasn’t been string enough? Surely it would be conceivable. – Ole V.V. Dec 24 '16 at 13:09
  • @OleV.V. - the problem is that there is significant runtime cost in implementing "special" values in integers ... unless this is supported in the hardware instruction set. Without hardware support, every integer arithmetic operation would entail a test to see if the operands were special. And most operations would also require an extra test to detect overflow. No language designer wants to cripple integer arithmetic performance on standard hardware. – Stephen C Dec 25 '16 at 11:24
  • Yes, @StephenC, I understood that from your answer. Thanks for repeating the good point. – Ole V.V. Dec 25 '16 at 11:27

4 Answers4

26

The integer types in Java use either unsigned binary (for char) or two's complement signed representation. There is no representation for "infinity" in either of these kinds of representations. For example, with int there are 2^32 possible values, and all of them represent finite numbers.

(Integer.MIN_VALUE is -231, Integer.MAX_VALUE is 231 - 1, and if you count them all ... including zero ... that makes 232 different values.)

By contrast, floating-point numbers are represented using IEEE binary floating-point representations, and these do have a standard way to represent both infinity and not-a-number values.

Therefore it makes sense to define POSITIVE_INFINITY and NEGATIVE_INFINITY constants for the floating-point types, and it is impossible to define them for the integer types.


If you wanted to know why it is like this:

  • The integer representations were designed / selected (a long time ago!) to maximize speed. Any special cases (like values reserved to represent infinity, etc.) would make the integer arithmetic hardware more complicated and slower. If the hardware designer's goal is to do an integer addition in one clock cycle, then making addition more complicated means that the clock speed must be slower. That effects the speed of the entire processor.

    The flip-side is that:

    • Overflow happens without any explicit notification (which may or may not be desirable)
    • Division by zero has to be dealt with via a hardware exception, and that results in a major performance penalty ... if it actually happens.
  • The standard committee that designed the IEEE floating-point representations were also taking account of the requirements of scientific and engineering domains where there was a need to be able to represent infinites. Floating point operations are already slower and more complicated because of the need to do scaling, etc. Therefore they most likely are already multi-cycle instructions, and there is probably some "slack" for dealing with the special cases.

    Also, there is the advantage that: INF and NaN values allow the operations that create them to proceed without a hardware exception, but without "sweeping the bad operations under the carpet" like with integer overflow.

Note that two's complement was used in a working computer in 1949 (EDSAC). The IEEE 754 standard emerged in 1985.


For what it is worth, some programming languages are aware of integer overflow; for example Ada. But they don't do this with representations of infinity, etc. Instead, they throw an exception (or equivalent) when when an operation overflows. Even so, this adds a performance penalty, since overflow detection typically entails an extra instruction after each integer arithmetic instruction to test an "overflow" status bit. (That's the way modern instruction sets work ...)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Integer types overflow and underflow. Floating points don't. – Bohemian Dec 24 '16 at 20:14
  • The speed consideration for integral types makes great sense. The performance penalty for checking special values on all operations might be very noticeable in an average program. – Ole V.V. Dec 25 '16 at 10:31
  • Some programming langauges do fail on integer overflow, and hardware support for that exists (similar to hardware support for failure on division by zero) (but this is really an aside). – Ole V.V. Dec 25 '16 at 10:32
6

It's part of the IEEE 754 floating-point standard, as mentioned in this spec:

The floating-point types are float and double, which are conceptually associated with the single-precision 32-bit and double-precision 64-bit format IEEE 754 values and operations as specified in IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Standard 754-1985 (IEEE, New York).

The IEEE 754 standard includes not only positive and negative numbers that consist of a sign and magnitude, but also positive and negative zeros, positive and negative infinities, and special Not-a-Number values (hereafter abbreviated NaN).

These special values are computed based on their bit representations according to the standard. For example, the Double positive infinity is computed based on the 0x7ff0000000000000 bit representation.

In contrast, integer types have no bit representation for infinite values. They only have representations for finite numbers. The Integer class defines the minimum and maximum finite values as -231 and 231-1.

Community
  • 1
  • 1
M A
  • 71,713
  • 13
  • 134
  • 174
3

As others have pointed out, it's in the IEEE specification, etc. Floats and doubles support NaN and Infinity, which integers do not.

In terms of the reasoning behind it, nothing is divisible by zero, and with integers you know that you are trying to divide by zero.

Floating point numbers are not exact. 0.003f - 0.001f - 0.002f is mathematically zero, but by the IEEE specification and our ability to represent numbers in computers, it's -2.3283064E-10. There's a finite number of decimal numbers you can represent in binary, and there isn't any representation that would allow us to always get a correct value for zero.

If tinyFloat == (0.003f - 0.001f - 0.002f) == -2.3283064E-10

That's mathematically zero and is practically zero, but 1f/tinyFloat == -4.2949673E9

// This still works too:
scala> Integer.MAX_VALUE / (tinyFloat * tinyFloat * tinyFloat)
res58: Float = -1.7014118E38

// But eventually you overflow
scala> Integer.MAX_VALUE / (tinyFloat * tinyFloat * tinyFloat * tinyFloat)
res59: Float = Infinity

(If you're not familiar, Scala is a JVM language, so the above value types are the same as Java.)

That last tinyFloat ^ 4 still isn't exactly zero, so it doesn't make sense for the computer to throw an ArithmeticException. This problem doesn't exist with integers. There's no other way to overflow with division. Integer.MAX_VALUE/1 is still Integer.MAX_VALUE. You either divided by zero, which is mathematically invalid and representable in binary, or you didn't, and got a valid result.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
nairbv
  • 4,045
  • 1
  • 24
  • 26
1

Mathematically, 1/0 = Infinity because we view division by zero as the limit(x -> 0) 1/x, furthermore with the implicit understanding that x is a positive quantity. Floating point division must support division by small quantities, and given that operations semantically come with a small margin of error, and given that that operation may include 0, it follows that throwing an exception due to division by a small quantity that due to rounding turned out to exactly equal to 0 is totally inappropriate.

Division by an integer zero really is undefined. If you divide by integer zero you probably shouldn't have. And performance would of course be severely compromised from doing this extra thing.

For comparison Integer supports Integer.MAX/MIN_VALUE. These should not be produced as a result of an operation but work for algorithms with steps like int minSoFar = Integer.MAX_VALUE; and the like.

djechlin
  • 59,258
  • 35
  • 162
  • 290