• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/writer/float_to_string.h"
16 
17 #include <cmath>
18 #include <cstring>
19 #include <functional>
20 #include <iomanip>
21 #include <limits>
22 #include <sstream>
23 
24 #include "src/debug.h"
25 
26 namespace tint {
27 namespace writer {
28 
FloatToString(float f)29 std::string FloatToString(float f) {
30   // Try printing the float in fixed point, with a smallish limit on the
31   // precision
32   std::stringstream fixed;
33   fixed.flags(fixed.flags() | std::ios_base::showpoint | std::ios_base::fixed);
34   fixed.precision(9);
35   fixed << f;
36 
37   // If this string can be parsed without loss of information, use it
38   auto float_equal_no_warning = std::equal_to<float>();
39   if (float_equal_no_warning(std::stof(fixed.str()), f)) {
40     auto str = fixed.str();
41     while (str.length() >= 2 && str[str.size() - 1] == '0' &&
42            str[str.size() - 2] != '.') {
43       str.pop_back();
44     }
45 
46     return str;
47   }
48 
49   // Resort to scientific, with the minimum precision needed to preserve the
50   // whole float
51   std::stringstream sci;
52   sci.precision(std::numeric_limits<float>::max_digits10);
53   sci << f;
54   return sci.str();
55 }
56 
FloatToBitPreservingString(float f)57 std::string FloatToBitPreservingString(float f) {
58   // For the NaN case, avoid handling the number as a floating point value.
59   // Some machines will modify the top bit in the mantissa of a NaN.
60 
61   std::stringstream ss;
62 
63   uint32_t float_bits = 0u;
64   std::memcpy(&float_bits, &f, sizeof(float_bits));
65 
66   // Handle the sign.
67   const uint32_t kSignMask = 1u << 31;
68   if (float_bits & kSignMask) {
69     // If `f` is -0.0 print -0.0.
70     ss << '-';
71     // Strip sign bit.
72     float_bits = float_bits & (~kSignMask);
73   }
74 
75   switch (std::fpclassify(f)) {
76     case FP_ZERO:
77     case FP_NORMAL:
78       std::memcpy(&f, &float_bits, sizeof(float_bits));
79       ss << FloatToString(f);
80       break;
81 
82     default: {
83       // Infinity, NaN, and Subnormal
84       // TODO(dneto): It's unclear how Infinity and NaN should be handled.
85       // See https://github.com/gpuweb/gpuweb/issues/1769
86 
87       // std::hexfloat prints 'nan' and 'inf' instead of an
88       // explicit representation like we want. Split it out
89       // manually.
90       const int kExponentBias = 127;
91       const int kExponentMask = 0x7f800000;
92       const int kMantissaMask = 0x007fffff;
93       const int kMantissaBits = 23;
94 
95       int mantissaNibbles = (kMantissaBits + 3) / 4;
96 
97       const int biased_exponent =
98           static_cast<int>((float_bits & kExponentMask) >> kMantissaBits);
99       int exponent = biased_exponent - kExponentBias;
100       uint32_t mantissa = float_bits & kMantissaMask;
101 
102       ss << "0x";
103 
104       if (exponent == 128) {
105         if (mantissa == 0) {
106           //  Infinity case.
107           ss << "1p+128";
108         } else {
109           //  NaN case.
110           //  Emit the mantissa bits as if they are left-justified after the
111           //  binary point.  This is what SPIRV-Tools hex float emitter does,
112           //  and it's a justifiable choice independent of the bit width
113           //  of the mantissa.
114           mantissa <<= (4 - (kMantissaBits % 4));
115           // Remove trailing zeroes, for tidyness.
116           while (0 == (0xf & mantissa)) {
117             mantissa >>= 4;
118             mantissaNibbles--;
119           }
120           ss << "1." << std::hex << std::setfill('0')
121              << std::setw(mantissaNibbles) << mantissa << "p+128";
122         }
123       } else {
124         // Subnormal, and not zero.
125         TINT_ASSERT(Writer, mantissa != 0);
126         const int kTopBit = (1 << kMantissaBits);
127 
128         // Shift left until we get 1.x
129         while (0 == (kTopBit & mantissa)) {
130           mantissa <<= 1;
131           exponent--;
132         }
133         // Emit the leading 1, and remove it from the mantissa.
134         ss << "1";
135         mantissa = mantissa ^ kTopBit;
136         mantissa <<= 1;
137         exponent++;
138 
139         // Emit the fractional part.
140         if (mantissa) {
141           // Remove trailing zeroes, for tidyness
142           while (0 == (0xf & mantissa)) {
143             mantissa >>= 4;
144             mantissaNibbles--;
145           }
146           ss << "." << std::hex << std::setfill('0')
147              << std::setw(mantissaNibbles) << mantissa;
148         }
149         // Emit the exponent
150         ss << "p" << std::showpos << std::dec << exponent;
151       }
152     }
153   }
154   return ss.str();
155 }
156 
157 }  // namespace writer
158 }  // namespace tint
159