mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-02 12:41:36 +00:00
Signed zeros are emitted. Subormal numbers are emitted as hex float. Handling Inf and NaN is unresolved in the spec. See https://github.com/gpuweb/gpuweb/issues/1769 NaN tests are disabled, due to platform dependence. Windows x86-64 seems to always set the high mantissa bit on NaNs. Fixed: tint:76 Change-Id: I06c69b93b04c869a63ec2f574022996acc7a6dbe Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/52321 Commit-Queue: David Neto <dneto@google.com> Auto-Submit: David Neto <dneto@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Ben Clayton <bclayton@google.com>
143 lines
4.2 KiB
C++
143 lines
4.2 KiB
C++
// Copyright 2020 The Tint Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "src/writer/float_to_string.h"
|
|
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <iomanip>
|
|
#include <limits>
|
|
#include <sstream>
|
|
|
|
#include "src/debug.h"
|
|
|
|
namespace tint {
|
|
namespace writer {
|
|
|
|
std::string FloatToString(float f) {
|
|
std::stringstream ss;
|
|
ss.flags(ss.flags() | std::ios_base::showpoint | std::ios_base::fixed);
|
|
ss.precision(std::numeric_limits<float>::max_digits10);
|
|
ss << f;
|
|
auto str = ss.str();
|
|
while (str.length() >= 2 && str[str.size() - 1] == '0' &&
|
|
str[str.size() - 2] != '.') {
|
|
str.pop_back();
|
|
}
|
|
return str;
|
|
}
|
|
|
|
std::string FloatToBitPreservingString(float f) {
|
|
// For the NaN case, avoid handling the number as a floating point value.
|
|
// Some machines will modify the top bit in the mantissa of a NaN.
|
|
|
|
std::stringstream ss;
|
|
|
|
uint32_t float_bits = 0u;
|
|
std::memcpy(&float_bits, &f, sizeof(float_bits));
|
|
|
|
// Handle the sign.
|
|
const uint32_t kSignMask = 1u << 31;
|
|
if (float_bits & kSignMask) {
|
|
// If `f` is -0.0 print -0.0.
|
|
ss << '-';
|
|
// Strip sign bit.
|
|
float_bits = float_bits & (~kSignMask);
|
|
}
|
|
|
|
switch (std::fpclassify(f)) {
|
|
case FP_ZERO:
|
|
case FP_NORMAL:
|
|
std::memcpy(&f, &float_bits, sizeof(float_bits));
|
|
ss << FloatToString(f);
|
|
break;
|
|
|
|
default: {
|
|
// Infinity, NaN, and Subnormal
|
|
// TODO(dneto): It's unclear how Infinity and NaN should be handled.
|
|
// See https://github.com/gpuweb/gpuweb/issues/1769
|
|
|
|
// std::hexfloat prints 'nan' and 'inf' instead of an
|
|
// explicit representation like we want. Split it out
|
|
// manually.
|
|
const int kExponentBias = 127;
|
|
const int kExponentMask = 0x7f800000;
|
|
const int kMantissaMask = 0x007fffff;
|
|
const int kMantissaBits = 23;
|
|
|
|
int mantissaNibbles = (kMantissaBits + 3) / 4;
|
|
|
|
const int biased_exponent =
|
|
static_cast<int>((float_bits & kExponentMask) >> kMantissaBits);
|
|
int exponent = biased_exponent - kExponentBias;
|
|
uint32_t mantissa = float_bits & kMantissaMask;
|
|
|
|
ss << "0x";
|
|
|
|
if (exponent == 128) {
|
|
if (mantissa == 0) {
|
|
// Infinity case.
|
|
ss << "1p+128";
|
|
} else {
|
|
// NaN case.
|
|
// Emit the mantissa bits as if they are left-justified after the
|
|
// binary point. This is what SPIRV-Tools hex float emitter does,
|
|
// and it's a justifiable choice independent of the bit width
|
|
// of the mantissa.
|
|
mantissa <<= (4 - (kMantissaBits % 4));
|
|
// Remove trailing zeroes, for tidyness.
|
|
while (0 == (0xf & mantissa)) {
|
|
mantissa >>= 4;
|
|
mantissaNibbles--;
|
|
}
|
|
ss << "1." << std::hex << std::setfill('0')
|
|
<< std::setw(mantissaNibbles) << mantissa << "p+128";
|
|
}
|
|
} else {
|
|
// Subnormal, and not zero.
|
|
TINT_ASSERT(mantissa != 0);
|
|
const int kTopBit = (1 << kMantissaBits);
|
|
|
|
// Shift left until we get 1.x
|
|
while (0 == (kTopBit & mantissa)) {
|
|
mantissa <<= 1;
|
|
exponent--;
|
|
}
|
|
// Emit the leading 1, and remove it from the mantissa.
|
|
ss << "1";
|
|
mantissa = mantissa ^ kTopBit;
|
|
mantissa <<= 1;
|
|
exponent++;
|
|
|
|
// Emit the fractional part.
|
|
if (mantissa) {
|
|
// Remove trailing zeroes, for tidyness
|
|
while (0 == (0xf & mantissa)) {
|
|
mantissa >>= 4;
|
|
mantissaNibbles--;
|
|
}
|
|
ss << "." << std::hex << std::setfill('0')
|
|
<< std::setw(mantissaNibbles) << mantissa;
|
|
}
|
|
// Emit the exponent
|
|
ss << "p" << std::showpos << std::dec << exponent;
|
|
}
|
|
}
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
} // namespace writer
|
|
} // namespace tint
|