OILS / mycpp / float_test.cc View on Github | oilshell.org

150 lines, 75 significant
1// float_test.cc - Learning by running code from Bruce Dawson's articles!
2//
3// Index here:
4// https://randomascii.wordpress.com/2013/02/07/float-precision-revisited-nine-digit-float-portability/
5//
6// union Float_t helper:
7// https://randomascii.wordpress.com/2012/01/11/tricks-with-the-floating-point-format/
8//
9// How to round trip floating numbers - just sprintf() and sscanf!
10// https://randomascii.wordpress.com/2012/03/08/float-precisionfrom-zero-to-100-digits-2/
11//
12// printf(“%1.8e\n”, d); // Round-trippable float, always with an exponent
13// printf(“%.9g\n”, d); // Round-trippable float, shortest possible
14//
15// printf(“%1.16e\n”, d); // Round-trippable double, always with an exponent
16// printf(“%.17g\n”, d); // Round-trippable double, shortest possible
17//
18// Good idea - do an exhaustive test of all floats:
19// https://randomascii.wordpress.com/2014/01/27/theres-only-four-billion-floatsso-test-them-all/
20//
21// But use threads. A single threaded test took 12 minutes on my machine.
22// https://randomascii.wordpress.com/2012/03/11/c-11-stdasync-for-fast-float-format-finding/
23
24#include <inttypes.h>
25
26#include "vendor/greatest.h"
27
28union Float_t {
29 Float_t(float num = 0.0f) : f(num) {
30 }
31 // Portable extraction of components.
32 bool Negative() const {
33 return (i >> 31) != 0;
34 }
35 int32_t RawMantissa() const {
36 return i & ((1 << 23) - 1);
37 }
38 int32_t RawExponent() const {
39 return (i >> 23) & 0xFF;
40 }
41
42 int32_t i;
43 float f;
44#if 1
45 struct { // Bitfields for exploration. Do not use in production code.
46 uint32_t mantissa : 23;
47 uint32_t exponent : 8;
48 uint32_t sign : 1;
49 } parts;
50#endif
51};
52
53void PrintPartsOfFloat(Float_t num) {
54 // printf("Float value, representation, sign, exponent, mantissa\n");
55 printf("%1.8e 0x%08X sign %d, exponent %d, mantissa 0x%06X\n", num.f,
56 num.i, num.parts.sign, num.parts.exponent, num.parts.mantissa);
57}
58
59// https://randomascii.wordpress.com/2013/02/07/float-precision-revisited-nine-digit-float-portability/
60
61TEST print_float_test() {
62 Float_t num(1.0f);
63 for (int i = 0; i < 10; ++i) {
64 PrintPartsOfFloat(num);
65
66 // change it to an adjacent float
67 num.i -= 1;
68
69#if 0
70 char s[20];
71
72 // He recommends both of these - what is the difference?
73 // Oh one of them uses e-01
74
75 sprintf(s, "%1.8e\n", num.f);
76 printf("%s", s);
77
78 sprintf(s, "%.9g\n", num.f);
79 printf("%s", s);
80
81 float f;
82 sscanf(s, "%f", &f);
83 Float_t parsed(f);
84
85 sprintf(s, "%.9g\n", parsed.f);
86 printf("parsed %s", s);
87
88 ASSERT_EQ_FMT(num.i, parsed.i, "%d");
89#endif
90 }
91
92 PASS();
93}
94
95TEST decimal_round_trip_test() {
96 // Test that sprintf() and sscanf() round trip all floats!
97 // Presumably strtof() uses the same algorithm as sscanf().
98
99 // This is the biggest number that can be represented in both float and
100 // int32_t. It’s 2^31-128.
101 Float_t max_float(2147483520.0f);
102
103 // 22 bits out of 32, so we print 2**10 or ~1000 lines of progress
104 const int interval = 1 << 22;
105
106 // Use long long to let us loop over all positive integers.
107 long long i = 0;
108 while (i <= max_float.i) {
109 Float_t num;
110 num.i = (int32_t)i;
111
112 char s[20];
113 sprintf(s, "%.9g\n", num.f); // preserves all information
114 // printf("%s", s);
115
116 float f;
117 sscanf(s, "%f", &f); // recover all information
118 Float_t parsed(f);
119
120 sprintf(s, "%.9g\n", parsed.f);
121 // printf("parsed %s", s);
122
123 ASSERT_EQ_FMT(num.i, parsed.i, "%d");
124
125 i++;
126
127 if (i % interval == 0) {
128 printf("%lld iterations done\n", i);
129 }
130
131 // Comment this out to do more
132 if (i == 1000) {
133 printf("STOPPING EARLY\n");
134 break;
135 }
136 }
137 PASS();
138}
139
140GREATEST_MAIN_DEFS();
141
142int main(int argc, char** argv) {
143 GREATEST_MAIN_BEGIN();
144
145 RUN_TEST(print_float_test);
146 RUN_TEST(decimal_round_trip_test);
147
148 GREATEST_MAIN_END();
149 return 0;
150}