Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversion of APFloat and string and back can change value #24913

Open
delcypher opened this issue Aug 21, 2015 · 1 comment
Open

Conversion of APFloat and string and back can change value #24913

delcypher opened this issue Aug 21, 2015 · 1 comment
Labels
bugzilla Issues migrated from bugzilla llvm:support

Comments

@delcypher
Copy link
Contributor

Bugzilla Link 24539
Version trunk
OS Linux
CC @ekatz,@sunfishcode

Extended Description

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

@ekatz
Copy link
Contributor

ekatz commented Nov 18, 2019

This is the proper behavior.
Even when using the native rounding modes, we get, when running the following program:

#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).

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla llvm:support
Projects
None yet
Development

No branches or pull requests

3 participants