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

Added Grisu3 algorithm support for double.ToString(). #14646

Merged
merged 6 commits into from Jan 29, 2018
@@ -10,6 +10,8 @@ set(BCLTYPE_SOURCES
bignum.cpp
currency.cpp
decimal.cpp
diyfp.cpp
grisu3.cpp
windowsruntimebufferhelper.cpp
number.cpp
oavariant.cpp
@@ -0,0 +1,75 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// File: diyfp.cpp
//

//

#include "diyfp.h"
#include "fp.h"

void DiyFp::Minus(const DiyFp& rhs)
{
_ASSERTE(m_e == rhs.e());
_ASSERTE(m_f >= rhs.f());

m_f -= rhs.f();
}

void DiyFp::Minus(const DiyFp& left, const DiyFp& right, DiyFp& result)
{
result = left;
result.Minus(right);
}

void DiyFp::Multiply(const DiyFp& rhs)
{
UINT64 m32 = 0xFFFFFFFF;

UINT64 a = m_f >> 32;
UINT64 b = m_f & m32;
UINT64 c = rhs.f() >> 32;
UINT64 d = rhs.f() & m32;

UINT64 ac = a * c;
UINT64 bc = b * c;
UINT64 ad = a * d;
UINT64 bd = b * d;

UINT64 tmp = (bd >> 32) + (ad & m32) + (bc & m32);
tmp += 1U << 31;

m_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
m_e = m_e + rhs.e() + SIGNIFICAND_LENGTH;
}

void DiyFp::Multiply(const DiyFp& left, const DiyFp& right, DiyFp& result)
{
result = left;
result.Multiply(right);
}

void DiyFp::GenerateNormalizedDiyFp(double value, DiyFp& result)
{
_ASSERTE(value > 0.0);

UINT64 f = 0;
int e = 0;
ExtractFractionAndBiasedExponent(value, &f, &e);

UINT64 normalizeBit = (UINT64)1 << 52;
while ((f & normalizeBit) == 0)
{
f <<= 1;
--e;
}

int lengthDiff = DiyFp::SIGNIFICAND_LENGTH - 53;
f <<= lengthDiff;
e -= lengthDiff;

result.SetSignificand(f);
result.SetExponent(e);
}
@@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// File: diyfp.h
//

//

#ifndef _DIYFP_H
#define _DIYFP_H

#include <clrtypes.h>

// An exteneded floating-point data structure.
// It defines a 64-bit significand and a 32-bit exponent,
// which is EXTENDED compare to IEEE double precision floating-point number.
class DiyFp

This comment has been minimized.

Copy link
@tannergooding

tannergooding Jan 4, 2018

Member

It would be helpful if this was more significantly documented.

What is it used for, why it is needed, and the reasoning for the specific format (96-bits, 64-bit significand, 32-bit exponent).

Most of it is not immediately obvious and will be initially confusing to anyone else coming to touch the code later.

This comment has been minimized.

Copy link
@mazong1123

mazong1123 Jan 4, 2018

Author Collaborator

OK will do.

{
public:
DiyFp()
: m_f(0), m_e()
{
}

DiyFp(UINT64 f, int e)
: m_f(f), m_e(e)
{
}

DiyFp(const DiyFp& rhs)
: m_f(rhs.m_f), m_e(rhs.m_e)
{
}

DiyFp& operator=(const DiyFp& rhs)
{
m_f = rhs.m_f;
m_e = rhs.m_e;

return *this;
}

UINT64 f() const
{
return m_f;
}

int e() const
{
return m_e;
}

void SetSignificand(UINT64 f)
{
m_f = f;
}

void SetExponent(int e)
{
m_e = e;
}

void Minus(const DiyFp& rhs);
static void Minus(const DiyFp& left, const DiyFp& right, DiyFp& result);

void Multiply(const DiyFp& rhs);
static void Multiply(const DiyFp& left, const DiyFp& right, DiyFp& result);

static void GenerateNormalizedDiyFp(double value, DiyFp& result);

public:
static const int SIGNIFICAND_LENGTH = 64;

private:
UINT64 m_f;

This comment has been minimized.

Copy link
@tannergooding

tannergooding Jan 4, 2018

Member

Just using f and e as the names is confusing without additional context indicating that they are the significand and exponent.

It also isn't clear if this supports a sign and whether or not the exponent is biased.

int m_e;
};

#endif
@@ -0,0 +1,67 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// File: fp.h
//

//

#ifndef _FP_H
#define _FP_H

#include <clrtypes.h>

struct FPSINGLE {
#if BIGENDIAN
unsigned int sign: 1;
unsigned int exp: 8;
unsigned int mant: 23;
#else
unsigned int mant: 23;
unsigned int exp: 8;
unsigned int sign: 1;
#endif
};

struct FPDOUBLE {
#if BIGENDIAN
unsigned int sign: 1;
unsigned int exp: 11;
unsigned int mantHi: 20;
unsigned int mantLo;
#else
unsigned int mantLo;
unsigned int mantHi: 20;
unsigned int exp: 11;
unsigned int sign: 1;
#endif
};

static void ExtractFractionAndBiasedExponent(double value, UINT64* f, int* e)
{
if (((FPDOUBLE*)&value)->exp != 0)
{
// For normalized value, according to https://en.wikipedia.org/wiki/Double-precision_floating-point_format

This comment has been minimized.

Copy link
@tannergooding

tannergooding Jan 4, 2018

Member

This is missing the sign handling and the case where the implicit significand bit is 0.

Edit: Nevermind, I see the case where the implicit significant bit is 0 is below.

// value = 1.fraction * 2^(exp - 1023)
// = (1 + mantissa / 2^52) * 2^(exp - 1023)
// = (2^52 + mantissa) * 2^(exp - 1023 - 52)
//
// So f = (2^52 + mantissa), e = exp - 1075;
*f = ((UINT64)(((FPDOUBLE*)&value)->mantHi) << 32) | ((FPDOUBLE*)&value)->mantLo + ((UINT64)1 << 52);
*e = ((FPDOUBLE*)&value)->exp - 1075;
}
else
{
// For denormalized value, according to https://en.wikipedia.org/wiki/Double-precision_floating-point_format
// value = 0.fraction * 2^(1 - 1023)
// = (mantissa / 2^52) * 2^(-1022)
// = mantissa * 2^(-1022 - 52)
// = mantissa * 2^(-1074)
// So f = mantissa, e = -1074
*f = ((UINT64)(((FPDOUBLE*)&value)->mantHi) << 32) | ((FPDOUBLE*)&value)->mantLo;
*e = -1074;
}
}

#endif // _FP_H
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.