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 |
|
28 | union 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 |
|
53 | void 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 |
|
61 | TEST 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 |
|
95 | TEST 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 |
|
140 | GREATEST_MAIN_DEFS();
|
141 |
|
142 | int 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 | }
|