Skip to content

Commit 6d34846

Browse files
committed
become a NaN master
1 parent 4801086 commit 6d34846

9 files changed

Lines changed: 268 additions & 120 deletions

File tree

‎c/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
1. [Subnormal numbers](interactive/subnormal.c)
118118
1. [float range](interactive/float_range.c)
119119
1. [math.h](math_h.c)
120+
1. [NaN](nan.c)
120121
1. [complex.h](complex_h.c)
121122
1. [varargs_h](varargs_h.c)
122123
1. [errno.h](errno_h.c)

‎c/float32.h‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#ifndef LKMC_FLOAT32_H
2+
#define LKMC_FLOAT32_H
3+
4+
#include <fenv.h> /* fetestexcept */
5+
#include <inttypes.h>
6+
#include <stdio.h>
7+
8+
/* Explicit byte array representation and printing of 32-bit float types. */
9+
10+
typedef struct {
11+
uint32_t sign, exponent, fraction;
12+
} LkmcFloat32;
13+
14+
LkmcFloat32 lkmc_float32_from_float(float f) {
15+
uint32_t bytes;
16+
LkmcFloat32 lkmc_float32;
17+
bytes = *(uint32_t*)&f;
18+
lkmc_float32.fraction = bytes & 0x007FFFFF;
19+
bytes >>= 23;
20+
lkmc_float32.exponent = bytes & 0x000000FF;
21+
bytes >>= 8;
22+
lkmc_float32.sign = bytes & 0x000000001;
23+
bytes >>= 1;
24+
return lkmc_float32;
25+
}
26+
27+
float lkmc_float32_from_bytes(
28+
uint32_t sign,
29+
uint32_t exponent,
30+
uint32_t fraction
31+
) {
32+
uint32_t bytes;
33+
bytes = 0;
34+
bytes |= sign;
35+
bytes <<= 8;
36+
bytes |= exponent;
37+
bytes <<= 23;
38+
bytes |= fraction;
39+
return *(float*)&bytes;
40+
}
41+
42+
int lkmc_float32_equal(
43+
float f,
44+
uint32_t sign,
45+
uint32_t exponent,
46+
uint32_t fraction
47+
) {
48+
LkmcFloat32 lkmc_float32;
49+
lkmc_float32 = lkmc_float32_from_float(f);
50+
return
51+
(lkmc_float32.sign == sign) &&
52+
(lkmc_float32.exponent == exponent) &&
53+
(lkmc_float32.fraction == fraction)
54+
;
55+
}
56+
57+
void lkmc_float32_print(float f) {
58+
LkmcFloat32 lkmc_float32 = lkmc_float32_from_float(f);
59+
printf(
60+
"0x%" PRIx32 " 0x%" PRIx32 " 0x%" PRIx32 "\n",
61+
lkmc_float32.sign, lkmc_float32.exponent, lkmc_float32.fraction
62+
);
63+
}
64+
65+
void lkmc_float32_print_exceptions_and_clear(void) {
66+
int n = fetestexcept(FE_ALL_EXCEPT);
67+
if (n) {
68+
if (n & FE_INVALID ) puts("FE_INVALID");
69+
if (n & FE_DIVBYZERO) puts("FE_DIVBYZERO");
70+
if (n & FE_INEXACT ) puts("FE_INEXACT");
71+
if (n & FE_INVALID ) puts("FE_INVALID");
72+
if (n & FE_OVERFLOW ) puts("FE_OVERFLOW");
73+
if (n & FE_UNDERFLOW) puts("FE_UNDERFLOW");
74+
}
75+
feclearexcept(FE_ALL_EXCEPT);
76+
}
77+
78+
#endif

‎c/interactive/subnormal.c‎

Lines changed: 13 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,75 +14,21 @@
1414

1515
#include <assert.h>
1616
#include <float.h> /* FLT_HAS_SUBNORM */
17-
#include <inttypes.h>
1817
#include <math.h> /* isnormal */
1918
#include <stdlib.h>
2019
#include <stdio.h>
2120

21+
#include "../float32.h"
22+
2223
#if FLT_HAS_SUBNORM != 1
2324
#error float does not have subnormal numbers
2425
#endif
2526

26-
typedef struct {
27-
uint32_t sign, exponent, fraction;
28-
} Float32;
29-
30-
Float32 float32_from_float(float f) {
31-
uint32_t bytes;
32-
Float32 float32;
33-
bytes = *(uint32_t*)&f;
34-
float32.fraction = bytes & 0x007FFFFF;
35-
bytes >>= 23;
36-
float32.exponent = bytes & 0x000000FF;
37-
bytes >>= 8;
38-
float32.sign = bytes & 0x000000001;
39-
bytes >>= 1;
40-
return float32;
41-
}
42-
43-
float float_from_bytes(
44-
uint32_t sign,
45-
uint32_t exponent,
46-
uint32_t fraction
47-
) {
48-
uint32_t bytes;
49-
bytes = 0;
50-
bytes |= sign;
51-
bytes <<= 8;
52-
bytes |= exponent;
53-
bytes <<= 23;
54-
bytes |= fraction;
55-
return *(float*)&bytes;
56-
}
57-
58-
int float32_equal(
59-
float f,
60-
uint32_t sign,
61-
uint32_t exponent,
62-
uint32_t fraction
63-
) {
64-
Float32 float32;
65-
float32 = float32_from_float(f);
66-
return
67-
(float32.sign == sign) &&
68-
(float32.exponent == exponent) &&
69-
(float32.fraction == fraction)
70-
;
71-
}
72-
73-
void float32_print(float f) {
74-
Float32 float32 = float32_from_float(f);
75-
printf(
76-
"%" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
77-
float32.sign, float32.exponent, float32.fraction
78-
);
79-
}
80-
8127
int main(void) {
8228
/* Basic examples. */
83-
assert(float32_equal(0.5f, 0, 126, 0));
84-
assert(float32_equal(1.0f, 0, 127, 0));
85-
assert(float32_equal(2.0f, 0, 128, 0));
29+
assert(lkmc_float32_equal(0.5f, 0, 126, 0));
30+
assert(lkmc_float32_equal(1.0f, 0, 127, 0));
31+
assert(lkmc_float32_equal(2.0f, 0, 128, 0));
8632
assert(isnormal(0.5f));
8733
assert(isnormal(1.0f));
8834
assert(isnormal(2.0f));
@@ -93,33 +39,33 @@ int main(void) {
9339
assert(2.0f == 0x1.0p1f);
9440

9541
/* Sign bit. */
96-
assert(float32_equal(-0.5f, 1, 126, 0));
97-
assert(float32_equal(-1.0f, 1, 127, 0));
98-
assert(float32_equal(-2.0f, 1, 128, 0));
42+
assert(lkmc_float32_equal(-0.5f, 1, 126, 0));
43+
assert(lkmc_float32_equal(-1.0f, 1, 127, 0));
44+
assert(lkmc_float32_equal(-2.0f, 1, 128, 0));
9945
assert(isnormal(-0.5f));
10046
assert(isnormal(-1.0f));
10147
assert(isnormal(-2.0f));
10248

10349
/* The special case of 0.0 and -0.0. */
104-
assert(float32_equal( 0.0f, 0, 0, 0));
105-
assert(float32_equal(-0.0f, 1, 0, 0));
50+
assert(lkmc_float32_equal( 0.0f, 0, 0, 0));
51+
assert(lkmc_float32_equal(-0.0f, 1, 0, 0));
10652
assert(!isnormal( 0.0f));
10753
assert(!isnormal(-0.0f));
10854
assert(0.0f == -0.0f);
10955

11056
/* ANSI C defines FLT_MIN as the smallest non-subnormal number. */
11157
assert(FLT_MIN == 0x1.0p-126f);
112-
assert(float32_equal(FLT_MIN, 0, 1, 0));
58+
assert(lkmc_float32_equal(FLT_MIN, 0, 1, 0));
11359
assert(isnormal(FLT_MIN));
11460

11561
/* The largest subnormal number. */
116-
float largest_subnormal = float_from_bytes(0, 0, 0x7FFFFF);
62+
float largest_subnormal = lkmc_float32_from_bytes(0, 0, 0x7FFFFF);
11763
assert(largest_subnormal == 0x0.FFFFFEp-126f);
11864
assert(largest_subnormal < FLT_MIN);
11965
assert(!isnormal(largest_subnormal));
12066

12167
/* The smallest non-zero subnormal number. */
122-
float smallest_subnormal = float_from_bytes(0, 0, 1);
68+
float smallest_subnormal = lkmc_float32_from_bytes(0, 0, 1);
12369
assert(smallest_subnormal == 0x0.000002p-126f);
12470
assert(0.0f < smallest_subnormal);
12571
assert(!isnormal(smallest_subnormal));

‎c/math_h.c‎

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -488,59 +488,6 @@ int main(void) {
488488
assert(1e100 < INFINITY);
489489
}
490490

491-
/*
492-
# NAN
493-
494-
Not a number.
495-
496-
Result of operations such as:
497-
498-
0.0 / 0.0
499-
INFINITY - INFINITY
500-
INFINITY * 0.o
501-
INFINITY / INFINITY
502-
503-
And any operation involving NAN.
504-
505-
The sign of NAN has no meaning.
506-
*/
507-
{
508-
/* [-]NAN or [-]NAN<more characters> implementation defined. */
509-
printf("NAN = %f\n", NAN);
510-
printf("-NAN = %f\n", -NAN);
511-
512-
/* TODO why do both fail? */
513-
/*assert(0 / f == -NAN);*/
514-
/*assert(0 / f == NAN);*/
515-
516-
volatile float f = 0;
517-
assert(isnan(0 / f));
518-
assert(isnan(NAN));
519-
assert(!isinf(NAN));
520-
521-
assert(isnan(NAN));
522-
assert(isnan(NAN + 1.0));
523-
assert(isnan(NAN + INFINITY));
524-
assert(isnan(NAN + NAN));
525-
assert(isnan(NAN - 1.0));
526-
assert(isnan(NAN * 2.0));
527-
assert(isnan(NAN / 1.0));
528-
assert(isnan(INFINITY - INFINITY));
529-
assert(isnan(INFINITY * 0.0));
530-
assert(isnan(INFINITY / INFINITY));
531-
532-
/*
533-
NAN is not ordered. any compairison to it yields false!
534-
535-
This is logical since 0 is neither smaller, larger or equal to NAN.
536-
*/
537-
{
538-
assert(!(0.0 < NAN));
539-
assert(!(0.0 > NAN));
540-
assert(!(0.0 == NAN));
541-
}
542-
}
543-
544491
#if __STDC_VERSION__ >= 199901L
545492
/*
546493
# isunordered

‎c/nan.c‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include <assert.h>
2+
#include <math.h> /* NAN, isnan */
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
6+
#include "float32.h"
7+
8+
int main(void) {
9+
float nanf0 = nanf("0");
10+
float nanf1 = nanf("1");
11+
float nanf2 = nanf("2");
12+
13+
/* Let's view the actual bytes for several NaNs. */
14+
puts("nanf0");
15+
lkmc_float32_print(nanf0);
16+
puts("nanf1");
17+
lkmc_float32_print(nanf1);
18+
puts("nanf2");
19+
lkmc_float32_print(nanf2);
20+
puts("-nanf0");
21+
lkmc_float32_print(-nanf0);
22+
puts("-nanf1");
23+
lkmc_float32_print(-nanf1);
24+
puts("-nanf2");
25+
lkmc_float32_print(-nanf2);
26+
puts("NAN");
27+
lkmc_float32_print(NAN);
28+
puts("-NAN");
29+
lkmc_float32_print(-NAN);
30+
puts("0.0f / 0.0f");
31+
lkmc_float32_print(0.0f / 0.0f);
32+
puts("1.0f / 0.0f");
33+
lkmc_float32_print(1.0f / 0.0f);
34+
puts("");
35+
36+
/* The exact printf representation is implementation defined.
37+
* glibc 2.23 only prints [+-]nan for all nans, so it is not
38+
* helpful to distinguish between them.
39+
*/
40+
printf("printf nanf(\"0\") = %f\n", nanf0);
41+
printf("printf nanf(\"1\") = %f\n", nanf1);
42+
printf("printf nanf(\"2\") = %f\n", nanf2);
43+
printf("printf -nanf(\"0\") = %f\n", -nanf0);
44+
45+
/* NANs are not ordered. any compairison to it yields false! */
46+
{
47+
assert(!(0.0 < NAN));
48+
assert(!(0.0 > NAN));
49+
assert(!(0.0 == NAN));
50+
assert( (0.0 != NAN));
51+
}
52+
53+
/* isnan determines if the results is a nan or not.
54+
*
55+
* All / almost all operations that take NaN as input give NaN as output.
56+
*/
57+
assert(isnan(0.0f / 0.0f));
58+
assert(isnan(NAN));
59+
assert(isnan(NAN + 1.0));
60+
assert(isnan(NAN + INFINITY));
61+
assert(isnan(NAN + NAN));
62+
assert(isnan(NAN - NAN));
63+
assert(isnan(NAN - 1.0));
64+
assert(isnan(NAN * 2.0));
65+
assert(isnan(NAN / 1.0));
66+
assert(isnan(INFINITY - INFINITY));
67+
assert(isnan(INFINITY * 0.0));
68+
assert(isnan(INFINITY / INFINITY));
69+
assert(!isinf(NAN));
70+
assert(isnan(sqrt(-1.0f)));
71+
72+
return EXIT_SUCCESS;
73+
}

‎cpp/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
1. [functional](functional.cpp)
9898
1. [iterator](iterator.cpp)
9999
1. [limits](limits.cpp)
100+
1. [NaN](nan.cpp)
100101
1. [memory](memory.cpp)
101102
1. [regex](regex.cpp)
102103
1. [utility](utility.cpp)

‎cpp/nan.cpp‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// C++ has some NaN functionality that C does not have,
2+
// we will illustrate those here.
3+
4+
#include <cassert>
5+
#include <cmath> // isnan
6+
#include <iostream>
7+
#include <limits> // std::numeric_limits
8+
9+
#include "../c/float32.h"
10+
11+
int main() {
12+
if (
13+
std::numeric_limits<float>::has_signaling_NaN &&
14+
std::numeric_limits<float>::has_quiet_NaN
15+
) {
16+
float snan = std::numeric_limits<float>::signaling_NaN();
17+
float qnan = std::numeric_limits<float>::quiet_NaN();
18+
float f;
19+
20+
std::cout << "snan" << std::endl;
21+
lkmc_float32_print(snan);
22+
std::cout << "qnan" << std::endl;
23+
lkmc_float32_print(qnan);
24+
25+
// No exceptions.
26+
assert(std::fetestexcept(FE_ALL_EXCEPT) == 0);
27+
28+
// Still no exceptions because qNaN.
29+
f = qnan + 1.0f;
30+
assert(std::isnan(f));
31+
assert(std::fetestexcept(FE_ALL_EXCEPT) == 0);
32+
33+
// Now we get an exception because sNaN.
34+
f = snan + 1.0f;
35+
assert(std::isnan(f));
36+
assert(std::fetestexcept(FE_ALL_EXCEPT) == FE_INVALID);
37+
feclearexcept(FE_ALL_EXCEPT);
38+
39+
}
40+
}

‎glibc/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@
2525
1. Interactive
2626
1. [`O_LARGEFILE`](interactive/o_largefile.c)
2727
1. [`O_TMPFILE`](interactive/o_tmpfile.c)
28+
1. [feenableexcept](interactive/feenableexcept.cpp)

0 commit comments

Comments
 (0)