Skip to content

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

Open
@delcypher

Description

@delcypher
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

Activity

ekatz

ekatz commented on Nov 18, 2019

@ekatz
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @delcypher@Endilll@ekatz

        Issue actions

          Conversion of APFloat and string and back can change value · Issue #24913 · llvm/llvm-project