ERights Home elang / scalars 
Back to: integer Type On to: boolean Type

Scalar Type:
float64


E's float64s are the subset of standard IEEE double precision floating point values specified by Java. This is identical to the IEEE standard except that there's only one (non-signalling) NaN value, and the only rounding mode supported is round-to-even. Therefore, a float64 can only have one of four kinds of values:

  • A real number, eg, 3.0
  • Positive Infinity, eg, the result of 1.0/0.0
  • Negative Infinity, eg, the result of -1.0/0.0
  • NaN (Not a Number), eg, the result of 0.0/0.0
Having only 64 bits, a float64 can only represent a finite set of real numbers. We often think of a floating point value as approximating a real number, but it's better to think of one as representing an exact real values, but only approximating arithmetic. Since only a finite set of reals can be represented, we might not be able to represent the real sum of a and b. If this sum is representable, IEEE, Java, and E all guarantee that "a + b" produce the value exactly representing this sum. Otherwise, round-to-even guarantees that we choose the value representing the nearest real number, with rules for tie breaking and edge conditions of concern only to the hard core numerical analyst.

Boolean Comparisons (non-associative)

Example Meaning Expansion Value
5.1 == 5.1 Are they the same? __equalizer.sameEver(5.1, 5.1) true
5.1 == 6.1 Are they the same? __equalizer.sameEver(5.1, 6.1) false
5.1 != 6.1 Are they different? __equalizer.sameEver(5.1, 6.1).not() true

As explained in When Are Two Things the Same?, "==" tests whether two values are computationally equivanelent. As applied to float64 values, leaving aside NaNs, -0.0, or mixing float64s with integers, this is the same as arithmetic equality (as defined by the IEEE floating point standard).

? pragma.syntax("0.8")

? 5.1 == 5.1
# value: true

You can use "<=>" operator to test for arithmetic equality:

The following cases are all those for which being the same differs from being arithmetically equal:

? NaN == NaN
# value: true

? NaN <=> NaN
# value: false

These are clearly the same, and E says so. However, according to the IEEE standard, NaNs are not arithmetically equal to anything, including themselves.

? 0.0 == -0.0
# value: false

? 0.0 <=> -0.0
# value: true

IEEE states that positive zero and negative zero are distinct values that are nevertheless arithmetically equal. Since they're distinct, they aren't the same, and E says so.

? 3.0 == 3
# value: false

? 3.0 <=> 3
# value: true

Of course, integer values are distinct from floating point values, even when they represent the same real number.

? 3 == 3.0
# value: false

? 3 <=> 3.0
# value: true

In all cases, "a != b" is equivalent to "!(a == b)".

Magnitude Comparisons (non-associative)

Example Meaning Expansion Value
5.1 < 6.1
less than
5.1.compareTo(6.1).belowZero()
true
5.1 <= 6.1
less or equal to
5.1.compareTo(6.1).atMostZero()
true
5.1 >= 6.1
greater or equal to
5.1.compareTo(6.1).atLeastZero()
false
5.1 > 6.1
greater than
5.1.compareTo(6.1).aboveZero()
false

On float64, these have the standard conventional meaning, but you may find this meaning surprising:

? 3.0 <= NaN
# value: false

? NaN <= 3.0
# value: false

? NaN >= 3.0
# value: false

? NaN <= NaN
# value: false

Yup. If at least one of the operands is a NaN, the answer's always false. This means that the double precision floating point values are not fully ordered, and many sorting algorithms many fail to sort them. All this applies equally to Java.

Interval Expressions (non-associative)

Example Meaning Expansion Value
5.0..9.0 the numbers from 5.0 inclusive to 9.0 inclusive __makeOrderedSpace.op__thru(5.0, 9.0) 5.0..9.0
5.0..!9.0 the numbers from 5.0 inclusive to 9.0 exclusive __makeOrderedSpace.op__till(5.0, 9.0) 5.0..!9.0

Whereas on integers, 5..!9 is equivalent to 5..8, on float64s 5.0..!9.0 includes all the double precision floating point numbers up to but excluding 9.0. It approximates the mathematical concept of a closed-open interval. We have yet to determine what the protocol is of the resulting float64 interval, but (unlike integer intervals) it cannot act like a ConstList, as it would be too exhausting to enumerate one.

Additive Expressions (left associative)

Example Meaning Expansion Value
5.0 + 6.0 addition 5.0.add(6.0) 11.0
5.0 - 6.0 subtraction 5.0.subtract(6.0) -1.0

As you'd expect.

Multiplicative Expressions (left associative)

Example Meaning Expansion Value
-5.1 * 3.1
multiplication
(-5.1).multiply(3.1)
-15.81
-5.1 / 3.1
floating-point division
(-5.1).approxDivide(3.1)
-1.64516
-5.1 // 3.1
integer division, rounding down
(-5.1).floorDivide(3.1)
-2
-5.1 % 3.1
remainder
(-5.1).remainder(3.1)
-2.0
-5.1 %% 3.1
modulo
(-5.1).mod(3.1)
1.1
2.1 ** 5.1 %% 3.1
modular exponentiation
2.1.modPow(5.1, 3.1)
0.586398

These mean the same as they do for integers. Notice that for both integers and float64s, "/" produces a float64 and "//" produces an integer.

Exponentiation Expression (non associative)

Example Meaning Expansion Value
2.0 ** -3.0
exponentiation
2.0.pow(-3.0)
0.125

As you'd expect.

Negation Expression (left associative)

Example Meaning Expansion Value
-5.0
negation
5.0.negate()
-5.0

As you'd expect.


Additional float64 Messages

In addition to the messages corresponding to the above operators, float64s respond to the following messages.

? (-5.1).truncDivide(3.1)
# value: -1

"truncDivide" is integer division where the answer is rounded to an integer by rounding towards zero. It correspond to Java's "/" operator on integers.

? (-5.1).ceil()
# value: -5

Nearest integer by rounding towards positive infinity.

? (-5.1).floor()
# value: -6

Nearest integer by rounding towards negative infinity.

? (-5.1).round()
# value: -5

? (-5.6).round()
# value: -6

The actually nearest integer.

? (-5.1).truncate()
# value: -5

? (-5.1).truncate()
# value: -5

Nearest integer by rounding towards zero.

? (-5.1).abs()
# value: 5.1

Absolute value.


Additional messages log, sin, cos, tan, sqrt, exp, asin, acos, atan, atan2(y), min(other), and max(other) are simply the corresponding static methods of java.lang.Math turned into instance methods of float64. See the javadoc-umentation of java.lang.Math for their specification.

Some useful constants and corresponding tests:

? NaN.isNaN()
# value: true
? Infinity.isInfinite()
# value: true
? def PI := (0.0.acos()) * 2.0
# value: 3.141592653589793

? def e := 1.0.exp()
# value: 2.7182818284590455


Mixing integers and float64s

Obsolete: E adopts a simple but strange rule regarding expressions of mixed arithmetic type: unless specified otherwise, the right operand must always adapt to the left operand. This is known as the left-side-wins rule. Integers are happy to adapt by automatically converting to float64. However, since there's no one right way for a float64 to convert to an integer, the programmer must specify:

? 3.1 + 5
# value: 8.1

? 3 + 5.1
# value: 8.1

? 3 + 5.1.round()
# value: 8

The chapter on capability programming style will explain how the left-side-wins rule accomodates both extensibility and security.

 
Unless stated otherwise, all text on this page which is either unattributed or by Mark S. Miller is hereby placed in the public domain.
ERights Home elang / scalars 
Back to: integer Type On to: boolean Type
Download    FAQ    API    Mail Archive    Donate

report bug (including invalid html)

Golden Key Campaign Blue Ribbon Campaign