// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include namespace cbor { // This is adapted from the example in RFC8949 appendix D. double DecodeHalfPrecisionFloat(uint16_t value) { uint16_t half = value; uint16_t exp = (half >> 10) & 0x1f; // 5 bit exponent uint16_t mant = half & 0x3ff; // 10 bit mantissa double val; if (exp == 0) { // Handle denormalized case val = std::ldexp(mant, -24); } else if (exp != 31) { // Handle normal case val = std::ldexp(mant + 1024, exp - 25); } else { // Handle special cases. if (mant == 0) { val = std::numeric_limits::infinity(); } else { val = std::numeric_limits::quiet_NaN(); } } // Handle sign bit. return half & 0x8000 ? -val : val; } uint16_t EncodeHalfPrecisionFloat(double input) { int exp = 0; uint16_t mantissa = 0; double abs_value = std::abs(input); // First handle special cases. if (!std::isfinite(input)) { // Set all-ones for exponent as a flag. exp = 0x1F; if (std::isnan(input)) { // A NaN value has a nonzero mantissa mantissa = 1; } // else - infinity } else if (abs_value == 0.0) { // This is a special case because it's not handled well with frexp(). exp = 0; mantissa = 0; } else { int normal_exp; double normal_value = std::frexp(abs_value, &normal_exp); // frexp returns numbers in the range [0.5,1) instead of the usual [1,2) // range used for the floating point mantissa so we need to offset the // exponent by one through the below. // Half-precision uses an offset of 15 for the exponent. We already have 1 // from the frexp so we just increase it by 14. exp = 14 + normal_exp; if (exp <= 0) { // Denormalized numbers. We don't remove the MSB in this case. mantissa = std::ldexp(normal_value, 10 + exp); exp = 0; } else { // Handle the normal case. We remove the MSB by subtracting 0.5. Then we // use an exponent of 11 here instead of 10 because we're in the [0.5, 1) // range and need the full 10 bits of precision. mantissa = std::ldexp(normal_value - 0.5, 11); } } uint16_t result = exp << 10 | mantissa; if (std::copysign(1, input) < 0) { result |= 0x8000; } return result; } } // namespace cbor