Extreme floating point values
You are encouraged to solve this task according to the task description, using any language you may know.
The IEEE floating point specification defines certain 'extreme' floating point values such as minus zero, -0.0, a value distinct from plus zero; not a number, NaN; and plus and minus infinity.
The task is to use expressions involving other 'normal' floating point values in your language to calculate these, (and maybe other), extreme floating point values in your language and assign them to variables. Print the values of these variables if possible; and show some arithmetic with these values and variables. If your language can directly enter these extreme floating point values then show it.
C.f:
- What Every Computer Scientist Should Know About Floating-Point Arithmetic
- Infinity
- Detect division by zero
Ada
The language specifies model floating-point numbers independent of the underlying hardware. Even if the machine numbers are IEEE 754, the user-defined floating-point numbers are guaranteed to have no IEEE 754 semantics. In particular, their values do not include any non-numeric ideals. Constraint_Error exception is propagated when the result of a numeric operation assigned to a floating-point variable is not in the range (the range is always numeric).
For performance reasons, the built-in floating-point types like Float and Long_Float are allowed to have IEEE 754 semantics if the machine numbers are IEEE 754. But the language provides means to exclude all non numbers from these types by defining a subtype with an explicit range: <lang Ada> subtype Consistent_Float is Float range Float'Range; -- No IEEE ideals </lang> In general in properly written Ada programs variables may not become invalid when standard numeric operations are applied. The language also provides the attribute 'Valid to verify values obtained from unsafe sources e.g. from input, unchecked conversions etc.
As stated above on a machine where Float is implemented by an IEEE 754 machine number, IEEE 754 is permitted leak through. The following program illustrates how this leak can be exploited: <lang Ada> with Ada.Text_IO; use Ada.Text_IO;
procedure IEEE is -- Non portable, bad, never do this!
Zero : Float := 0.0; PInf : Float := 1.0 / Zero; NInf : Float := -PInf; PZero : Float := 1.0 / PInf; NZero : Float := 1.0 / NInf; NaN : Float := 0.0 / Zero;
begin
Put_Line (" -oo = " & Float'Image (NInf)); Put_Line (" +oo = " & Float'Image (PInf)); Put_Line (" NaN = " & Float'Image (NaN)); Put_Line (" -0 = " & Float'Image (NZero));
Put_Line (" -oo < first " & Boolean'Image (NInf < Float'First)); Put_Line (" +oo > last " & Boolean'Image (PInf > Float'Last)); Put_Line (" NaN = NaN " & Boolean'Image (NaN = NaN)); Put_Line (" -0 = 0 " & Boolean'Image (NZero = 0.0)); Put_Line (" +0 = 0 " & Boolean'Image (PZero = 0.0)); Put_Line (" +0 < least positive " & Boolean'Image (PZero < Float'Succ (Zero))); Put_Line (" -0 > biggest negative " & Boolean'Image (NZero > Float'Pred (Zero)));
-- Validness checks Put_Line ("Valid -oo is " & Boolean'Image (NInf'Valid)); Put_Line ("Valid +oo is " & Boolean'Image (PInf'Valid)); Put_Line ("Valid NaN is " & Boolean'Image (NaN'Valid));
end IEEE; </lang> The expression -1.0 / 0.0 were non-numeric and thus could not be used. To fool the compiler the variable Zero is used, which circumvents type checks giving desired broken result. Sample output:
-oo = -Inf******* +oo = +Inf******* NaN = NaN******** -0 = -0.00000E+00 -oo < first TRUE +oo > last TRUE NaN = NaN FALSE -0 = 0 TRUE +0 = 0 TRUE +0 < least positive TRUE -0 > biggest negative TRUE Valid -oo is FALSE Valid +oo is FALSE Valid NaN is FALSE
C
<lang C>
- include <stdio.h>
int main() {
float inf = 1/0.0; float minus_inf = -1/0.0; float minus_zero = -1/ inf ; float nan = 0.0/0.0;
printf("positive infinity: %f\n",inf); printf("negative infinity: %f\n",minus_inf); printf("negative zero: %f\n",minus_zero); printf("not a number: %f\n",nan);
/* some arithmetic */
printf("+inf + 2.0 = %f\n",inf + 2.0); printf("+inf - 10.1 = %f\n",inf - 10.1); printf("+inf + -inf = %f\n",inf + minus_inf); printf("0.0 * +inf = %f\n",0.0 * inf); printf("1.0/-0.0 = %f\n",1.0/minus_zero); printf("NaN + 1.0 = %f\n",nan + 1.0); printf("NaN + NaN = %f\n",nan + nan);
return 0;
} </lang> Output:
positive infinity: inf negative infinity: -inf negative zero: -0.000000 not a number: -nan +inf + 2.0 = inf +inf - 10.1 = inf +inf + -inf = -nan 0.0 * +inf = -nan 1.0/-0.0 = -inf NaN + 1.0 = -nan NaN + NaN = -nan
D
D V.2 has a pretty comprehensive approach to floating point values, and unlike Ada embraces IEEE 754. This program shows only part of the floating point features supported by D and its Phobos standard library. <lang d>import std.stdio: writeln, writefln; import std.string: format; import std.math: NaN, getNaNPayload;
string toHex(T)(T x) {
string result; ubyte* ptr = cast(ubyte*)&x; foreach (i; 0 .. T.sizeof) result = format("%02x", ptr[i]) ~ result; return result;
}
void main() {
{ writeln("Computed extreme float values:"); float zero = 0.0f; float pos_inf = 1.0f / zero; writeln(" float +oo = ", pos_inf);
float neg_inf = -pos_inf; writeln(" float -oo = ", neg_inf);
float pos_zero = 1.0f / pos_inf; writeln(" float +0 = ", pos_zero);
float neg_zero = 1.0f / neg_inf; writeln(" float -0 = ", neg_zero);
float nan = zero / pos_zero; writefln(" float init = %f %s", nan, toHex(nan)); writeln();
writeln("Some float properties and literals:"); writeln(" float +oo = ", float.infinity); writeln(" float -oo = ", -float.infinity); writeln(" float +0 = ", 0.0f); writeln(" float -0 = ", -0.0f); writefln(" float nan = %f %s", float.nan, toHex(float.nan)); writefln(" float init = %f %s", float.init, toHex(float.init)); writeln(" float epsilon = ", float.epsilon); writeln(" float max = ", float.max); writeln(" float -max = ", -float.max); writeln(" float min_normal = ", -float.min_normal);
}
writeln("-----------------------------");
{ writeln("Computed extreme double values:"); double zero = 0.0; double pos_inf = 1.0 / zero; writeln(" double +oo = ", pos_inf);
double neg_inf = -pos_inf; writeln(" double -oo = ", neg_inf);
double pos_zero = 1.0 / pos_inf; writeln(" double +0 = ", pos_zero);
double neg_zero = 1.0 / neg_inf; writeln(" double -0 = ", neg_zero);
double nan = zero / pos_zero; writefln(" double init = %f %s", nan, toHex(nan)); writeln();
writeln("Some double properties and literals:"); writeln(" double +oo = ", double.infinity); writeln(" double -oo = ", -double.infinity); writeln(" double +0 = ", 0.0); writeln(" double -0 = ", -0.0); writefln(" double nan = %f %s", double.nan, toHex(double.nan)); writefln(" double init = %f %s", double.init, toHex(double.init)); writeln(" double epsilon = ", double.epsilon); writeln(" double max = ", double.max); writeln(" double -max = ", -double.max); writeln(" double min_normal = ", -double.min_normal); }
writeln("-----------------------------"); { writeln("Computed extreme real values:"); real zero = 0.0L; real pos_inf = 1.0L / zero; writeln(" real +oo = ", pos_inf);
real neg_inf = -pos_inf; writeln(" real -oo = ", neg_inf);
real pos_zero = 1.0L / pos_inf; writeln(" real +0 = ", pos_zero);
real neg_zero = 1.0L / neg_inf; writeln(" real -0 = ", neg_zero);
real nan = zero / pos_zero; writefln(" real init = %f %s", nan, toHex(nan)); writeln();
writeln("Some real properties and literals:"); writeln(" real +oo = ", real.infinity); writeln(" real -oo = ", -real.infinity); writeln(" real +0 = ", 0.0L); writeln(" real -0 = ", -0.0L); writefln(" real nan = %f %s", real.nan, toHex(real.nan)); writefln(" real init = %f %s", real.init, toHex(real.init)); writeln(" real epsilon = ", real.epsilon); writeln(" real max = ", real.max); writeln(" real -max = ", -real.max); writeln(" real min_normal = ", -real.min_normal); }
writeln("-----------------------------");
writeln("Largest possible payload for float NaN, double NaN, real NaN:"); float f1 = NaN(0x3F_FFFF); writeln(getNaNPayload(f1)); double f2 = NaN(0x3_FFFF_FFFF_FFFF); writeln(getNaNPayload(f2)); real f3 = NaN(0x3FFF_FFFF_FFFF_FFFF); writeln(getNaNPayload(f3));
}</lang> Output:
Computed extreme float values: float +oo = inf float -oo = -inf float +0 = 0 float -0 = -0 float init = -nan ffc00000 Some float properties and literals: float +oo = inf float -oo = -inf float +0 = 0 float -0 = -0 float nan = nan 7fc00000 float init = nan 7fa00000 float epsilon = 1.19209e-07 float max = 3.40282e+38 float -max = -3.40282e+38 float min_normal = -1.17549e-38 ----------------------------- Computed extreme double values: double +oo = inf double -oo = -inf double +0 = 0 double -0 = -0 double init = -nan fff8000000000000 Some double properties and literals: double +oo = inf double -oo = -inf double +0 = 0 double -0 = -0 double nan = nan 7ff8000000000000 double init = nan 7ff4000000000000 double epsilon = 2.22045e-16 double max = 1.79769e+308 double -max = -1.79769e+308 double min_normal = -2.22507e-308 ----------------------------- Computed extreme real values: real +oo = inf real -oo = -inf real +0 = 0 real -0 = -0 real init = -nan ffffc000000000000000 Some real properties and literals: real +oo = inf real -oo = -inf real +0 = 0 real -0 = -0 real nan = nan 7fffc000000000000000 real init = nan 7fffa000000000000000 real epsilon = 1.0842e-19 real max = 1.18973e+4932 real -max = -1.18973e+4932 real min_normal = -3.3621e-4932 ----------------------------- Largest possible payload for float NaN, double NaN, real NaN: 4194303 1125899906842623 4610560118520545279
Among other things, it is possible to trap FP hardware exceptions: <lang d>import std.math: FloatingPointControl;
void main() {
// Enable hardware exceptions for division by zero, overflow to infinity, // invalid operations, and uninitialized floating-point variables. FloatingPointControl fpc; fpc.enableExceptions(FloatingPointControl.severeExceptions);
double f0 = 0.0; double y1 = f0 / f0; // generates hardware exception
}</lang> Output:
object.Error: Invalid Floating Point Operation
Forth
<lang forth> 1e 0e f/ f. \ inf -1e 0e f/ f. \ inf (output bug: should say "-inf") -1e 0e f/ f0< . \ -1 (true, it is -inf)
0e 0e f/ f. \ nan
-1e 0e f/ 1/f f0< . \ 0 (false, can't represent IEEE negative zero)</lang>
J
Extreme values <lang j> Inf=: _
NegInf=: __ NB. Negative zero can not be represented in J. NaN=. _.</lang>
The numeric atom _.
(Indeterminate) is provided as a means
for dealing with NaN in data from sources outside J.
J itself generates NaN errors rather than NaN values and recommends that _.
be removed from data as soon as possible because, by definition, they will produce inconsistent results in contexts where value is important.
Extreme values from expressions <lang j> (1 % 0) , (_1 % 0) _ __
(1e234 * 1e234) , (_1e234 * 1e234)
_ __
_ + __ NB. generates NaN error, rather than NaN
|NaN error | _ +__
_ - _ NB. generates NaN error, rather than NaN
|NaN error | _ -_</lang>
Some arithmetic <lang j> _ + _ _
__ + __
__
Inf + 0
_
NegInf * 0
0</lang>
Java
<lang java>public class Extreme {
public static void main(String[] args) { double negInf = -1.0 / 0.0; //also Double.NEGATIVE_INFINITY double inf = 1.0 / 0.0; //also Double.POSITIVE_INFINITY double nan = 0.0 / 0.0; //also Double.NaN double negZero = -2.0 / inf;
System.out.println("Negative inf: " + negInf); System.out.println("Positive inf: " + inf); System.out.println("NaN: " + nan); System.out.println("Negative 0: " + negZero); System.out.println("inf + -inf: " + (inf + negInf)); System.out.println("0 * NaN: " + (0 * nan)); System.out.println("NaN == NaN: " + (nan == nan)); }
}</lang> Output:
Negative inf: -Infinity Positive inf: Infinity NaN: NaN Negative 0: -0.0 inf + -inf: NaN 0 * NaN: NaN NaN == NaN: false
MUMPS
ANSI MUMPS
The 1995 Standard MUMPS (X11.1–1995) implementations do not deal with floating point numbers following IEEE 754. Attempting to use a number over the precision of the system results in a <MAXNUMBER> error:
USER>write 3e145 30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 USER>write 3e146 <MAXNUMBER>
Intersystems Caché
Caché has the function $DOUBLE which complies with the IEEE 754 standard. The negative zero is indistinguishable from positive zero by operations. The special values evaluate to 0 when converted to a number in a later operation. <lang MUMPS> EXTREMES
NEW INF,NINF,ZERO,NOTNUM,NEGZERO SET INF=$DOUBLE(3.0E310),NINF=$DOUBLE(-3.0E310),ZERO=$DOUBLE(0),NOTNUM=$DOUBLE(INF-INF),NEGZERO=$DOUBLE(ZERO*-1) WRITE "Infinity: ",INF,! WRITE "Infinity ",$SELECT($ISVALIDNUM(INF):"is a number",1:"is not a number"),! WRITE "Negative Infinity: ",NINF,! WRITE "Negative Infinity ",$SELECT($ISVALIDNUM(NINF):"is a number",1:"is not a number"),! WRITE "Zero: ",ZERO,! WRITE "Zero ",$SELECT($ISVALIDNUM(ZERO):"is a number",1:"is not a number"),! WRITE "Negative Zero: ",NEGZERO,! WRITE "Negative Zero ",$SELECT($ISVALIDNUM(NEGZERO):"is a number",1:"is not a number"),! WRITE "Not a Number: ",NOTNUM,! WRITE "Not a Number ",$SELECT($ISVALIDNUM(NOTNUM):"is a number",1:"is not a number"),! KILL INF,NINF,ZERO,NONNUM,NEGZERO QUIT
</lang>
Output:
USER>d EXTREMES^ROSETTA Infinity: INF Infinity is not a number Negative Infinity: -INF Negative Infinity is not a number Zero: 0 Zero is a number Negative Zero: 0 Negative Zero is a number Not a Number: NAN Not a Number is not a number
OCaml
<lang ocaml># infinity;; - : float = infinity
- neg_infinity;;
- : float = neg_infinity
- nan;;
- : float = nan
- -0.;;
- : float = -0.
- -. 0.;;
- : float = -0.
- 1. /. 0.;;
- : float = infinity
- -1. /. 0.;;
- : float = neg_infinity
- -. infinity;;
- : float = neg_infinity
- infinity +. neg_infinity;;
- : float = nan
- 0. /. 0.;;
- : float = nan
- infinity /. infinity;;
- : float = nan
- nan = nan;;
- : bool = false
- nan == nan;;
- : bool = true
- 0. *. infinity;;
- : float = nan
- 0. = -0.;;
- : bool = true
- 0. == -0.;;
- : bool = false</lang>
Oz
<lang oz>declare Inf = 1.0e234 * 1.0e234 MinusInf = 1.0e234 * ~1.0e234 Zero = 1.0 / Inf MinusZero = 1.0 / MinusInf NaN = 0.0 / 0.0
{System.showInfo "infinite: "#Inf} {System.showInfo "-infinite: "#MinusInf} {System.showInfo "0: "#Zero} {System.showInfo "-0: "#MinusZero} %% seems to be identical to Zero {System.showInfo "NaN: "#NaN}
{System.showInfo "inf + -inf: "#Inf+MinusInf} {System.showInfo "NaN * 0: "#NaN*0.0} {System.showInfo "0 * NaN: "#0.0*NaN} {System.showInfo "inf * 0: "#Inf*0.0} {System.showInfo "0 * inf: "#0.0*Inf}
{Show NaN == NaN} %% shows 'true' ! {Show Zero == MinusZero}
{Show 1.0/0.0 == Inf} %% true {Show 1.0/~0.0 == MinusInf} %% true</lang>
Output: <lang oz>infinite: 1.#INF -infinite: -1.#INF 0: 0.0 -0: 0.0 NaN: -1.#IND inf + -inf: -1.#IND NaN * 0: -1.#IND 0 * NaN: -1.#IND inf * 0: -1.#IND 0 * inf: -1.#IND true true true true</lang>
PureBasic
<lang PureBasic>Define.f If OpenConsole()
inf = 1/None minus_inf = -1/None minus_zero = -1/inf nan = None/None PrintN("positive infinity: "+StrF(inf)) PrintN("negative infinity: "+StrF(minus_inf)) PrintN("positive zero: "+StrF(None)) PrintN("negative zero: "+StrF(minus_zero)) ; handles as 0.0 PrintN("not a number: "+StrF(nan)) PrintN("Arithmetics") PrintN("+inf + 2.0 = "+StrF(inf + 2.0)) PrintN("+inf - 10.1 = "+StrF(inf - 10.1)) PrintN("+inf + -inf = "+StrF(inf + minus_inf)) PrintN("0.0 * +inf = "+StrF(0.0 * inf)) PrintN("1.0/-0.0 = "+StrF(1.0/minus_zero)) PrintN("NaN + 1.0 = "+StrF(nan + 1.0)) PrintN("NaN + NaN = "+StrF(nan + nan)) PrintN("Logics") If IsInfinity(inf): PrintN("Variabel 'Infinity' is infenite"): EndIf If IsNAN(nan): PrintN("Variable 'nan' is not a number"): EndIf Print(#CRLF$+"Press ENTER to EXIT"): Input()
EndIf</lang>
positive infinity: +Infinity negative infinity: -Infinity positive zero: 0.0000000000 negative zero: 0.0000000000 not a number: -1.#IND000000 Arithmetics +inf + 2.0 = +Infinity +inf - 10.1 = +Infinity +inf + -inf = -1.#IND000000 0.0 * +inf = -1.#IND000000 1.0/-0.0 = -Infinity NaN + 1.0 = -1.#IND000000 NaN + NaN = -1.#IND000000 Logics Variabel 'Infinity' is infenite Variable 'nan' is not a number Press ENTER to EXIT
Python
<lang python>>>> # Extreme values from expressions >>> inf = 1e234 * 1e234 >>> _inf = 1e234 * -1e234 >>> _zero = 1 / _inf >>> nan = inf + _inf >>> inf, _inf, _zero, nan (inf, -inf, -0.0, nan) >>> # Print >>> for value in (inf, _inf, _zero, nan): print (value)
inf -inf -0.0 nan >>> # Extreme values from other means >>> float('nan') nan >>> float('inf') inf >>> float('-inf') -inf >>> -0. -0.0 >>> # Some arithmetic >>> nan == nan False >>> nan is nan True >>> 0. == -0. True >>> 0. is -0. False >>> inf + _inf nan >>> 0.0 * nan nan >>> nan * 0.0 nan >>> 0.0 * inf nan >>> inf * 0.0 nan</lang>
<lang python>>>> # But note! >>> 1 / -0.0
Traceback (most recent call last):
File "<pyshell#106>", line 1, in <module> 1 / -0.0
ZeroDivisionError: float division by zero >>> # (Not minus infinity)</lang>
Tcl
Tcl includes support in expressions for all IEEE “extreme” values except for NaN, which it throws a catchable exception on encountering numerically. Moreover, all can be just written directly as literals (they are parsed case-insensitively). For example, see this log of an interactive session: <lang tcl>% package require Tcl 8.5 8.5.2 % expr inf+1 Inf % set inf_val [expr {1.0 / 0.0}] Inf % set neginf_val [expr {-1.0 / 0.0}] -Inf % set negzero_val [expr {1.0 / $neginf_val}] -0.0 % expr {0.0 / 0.0} domain error: argument not in valid range % expr nan domain error: argument not in valid range % expr {1/-inf} -0.0</lang> It is possible to introduce a real NaN though numeric computation, but only by using the mechanisms for dealing with external binary data (it being judged better to just deal with it in that case rather than throwing an exception): <lang tcl>% binary scan [binary format q nan] q nan 1 % puts $nan NaN % # Show that it is a real NaN in there % expr {$nan+0} can't use non-numeric floating-point value as operand of "+"</lang>