Dan Liew via llvm-dev
2015-Aug-10 18:05 UTC
[llvm-dev] Bug or expected behavior of APFloat class?
Hi, I've been playing around with the APFloat class lately and I came across behavior I was not expecting based on reading the implementation comments and I'm wondering if it's a bug or intentional. The behavior concerns converting an APFloat to a string and back again. 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. So I'm wondering if the comment about round tripping APFloats only holds for certain rounding modes. Any thoughts on this? Thanks, Dan
Hal Finkel via llvm-dev
2015-Aug-16 03:19 UTC
[llvm-dev] Bug or expected behavior of APFloat class?
----- Original Message -----> From: "Dan Liew via llvm-dev" <llvm-dev at lists.llvm.org> > To: llvm-dev at lists.llvm.org > Sent: Monday, August 10, 2015 1:05:59 PM > Subject: [llvm-dev] Bug or expected behavior of APFloat class? > > Hi, > > I've been playing around with the APFloat class lately and I came > across behavior I was not expecting based on reading the > implementation comments and I'm wondering if it's a bug or > intentional. > > The behavior concerns converting an APFloat to a string and back > again. 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. > So I'm wondering if the comment about round tripping APFloats only > holds for certain rounding modes. > > Any thoughts on this?It is certainly possible that APFloat::rmNearestTiesToEven is required for the current code to work (that's the default mode that most of LLVM assumes). That certainly seems like a bug, and I'm not surprised if you find bugs when using other rounding modes. -Hal> > Thanks, > Dan > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org http://llvm.cs.uiuc.edu > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-- Hal Finkel Assistant Computational Scientist Leadership Computing Facility Argonne National Laboratory
Dan Liew via llvm-dev
2015-Aug-21 18:10 UTC
[llvm-dev] Bug or expected behavior of APFloat class?
>> Any thoughts on this? > > It is certainly possible that APFloat::rmNearestTiesToEven is required for the current code to work (that's the default mode that most of LLVM assumes). That certainly seems like a bug, and I'm not surprised if you find bugs when using other rounding modes.Thanks for the reply. I don't really have time to look into the details of this but I've opened up a bug report in case someone does want to take a look at it in the future. https://llvm.org/bugs/show_bug.cgi?id=24539