This was discussed on mailing list [1]. The bug seems to be that the conversion of an APFloat to a string (using the "natural precision") and then converting that back into an APFloat can result in a different value when not using ``APFloat::rmNearestTiesToEven``. In the implementation of ``APFloat::toString(...)`` you can specify ``FormatPrecision`` as 0. The method comments state that this will use the "natural precision" of the number. In the actual implementation the comments say that when FormatPrecision is 0 that ``` // We use enough digits so the number can be round-tripped back to an // APFloat. The formula comes from "How to Print Floating-Point Numbers // Accurately" by Steele and White. ``` Based on the above comments I expected to be able to convert an APFloat to a string and back again when ``FormatPrecision`` is set to zero. However this does not seem to hold. Here's some example code that demonstrates this. ``` #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/APFloat.h" #include <string> using namespace llvm; std::string getString(APFloat f) { SmallVector<char,10> strRep; // FormatPrecision=0 means that the "natural precision" of the number is used f.toString(strRep,/*FormatPrecision=*/0, /*FormatMaxPadding=*/0); return std::string(strRep.begin(), strRep.end()); } uint16_t getBits(APFloat f) { APInt bits = f.bitcastToAPInt(); assert(bits.getActiveBits() <= 16); return (uint16_t) (bits.getZExtValue() & 0xffff); } int main(int argc, char** argv) { APFloat f(APFloat::IEEEhalf); APFloat newF(APFloat::IEEEhalf); f.convertFromString("0.3", APFloat::rmTowardZero); outs() << "f bits: 0x"; outs().write_hex(getBits(f)); outs() << "\n"; assert(getBits(f) == 0x34cc); // Check that if we get the string using FormatPrecision=0 // that this can be used to construct another APFloat of the // same value. std::string fAsString = getString(f); outs() << "f as string: \"" << fAsString << "\"\n"; newF.convertFromString(fAsString, APFloat::rmTowardZero); outs() << "newF as string: \"" << getString(newF) << "\"\n"; outs() << "newF bits: 0x"; outs().write_hex(getBits(newF)); outs() << "\n"; // BUG This assert fails assert(getBits(newF) == 0x34cc); return 0; } ``` The output I see is ``` f bits: 0x34cc f as string: "2.998E-1" newF as string: "2.9956E-1" newF bits: 0x34cb ... Assertion `getBits(newF) == 0x34cc' failed. ``` As you can see when we create a new APFloat from the string we get a slightly smaller number. I have observed that if I use ``APFloat::rmNearestTiesToEven`` when creating ``newF`` that I do get an APFloat instance which has the same value as the original APFloat. [1] http://lists.llvm.org/pipermail/llvm-dev/2015-August/089085.html
This is the proper behavior. Even when using the native rounding modes, we get, when running the following program: ```c++ #include <cstdio> #include <cfenv> #include <sstream> #include <string> using namespace std; string to_str (float f) { ostringstream os; os.precision(9); os << f; return os.str (); } double d = 0.3; int main() { float f; string str; fesetround(FE_TONEAREST); f = d; str = to_string(f); printf("%08x = %.9g -> %s\n", *(int*)&f, f, str.c_str()); f = stof(str); printf("%08x = %.9g\n", *(int*)&f, f); fesetround(FE_TOWARDZERO); f = d; str = to_string(f); printf("%08x = %.9g -> %s\n", *(int*)&f, f, str.c_str()); f = stof(str); printf("%08x = %.9g\n", *(int*)&f, f); } ``` The output is: 3e99999a = 0.300000012 -> 0.300000012 3e99999a = 0.300000012 3e999999 = 0.299999982 -> 0.299999982 3e999998 = 0.299999952 Which is exactly as in APFloat (for IEEEsingle).