This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Added Grisu3 algorithm support for double.ToString(). #14646
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
53500f5
Added Grisu3 algorithm support for double.ToString().
mazong1123 b301d71
Added comments. Fixed a minor bug.
mazong1123 92cad70
Optimized the performance in DigitGen.
mazong1123 72e8b70
Merge remote-tracking branch 'upstream/master' into fix-14478-grisu
mazong1123 448787f
Added shortcut for numbers without fraction.
mazong1123 8e22bbe
Updated according to the review comments.
mazong1123 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// 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 which is required by Grisu3 algorithm. | ||
// It defines a 64-bit significand and a 32-bit exponent, | ||
// which is EXTENDED compare to IEEE double precision floating-point number (53 bits significand and 11 bits exponent). | ||
// | ||
// Original Grisu algorithm produces suboptimal results. To shorten the output (which is part of Grisu2/Grisu3's job), | ||
// we need additional 11 bits of the significand f and larger exponent e (A larger exponent range is used to avoid overflow. A 32-bit exponent is far big enough). | ||
// To fully understand how Grisu3 uses this data structure to get better result, please read http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf | ||
class DiyFp | ||
{ | ||
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: | ||
// Extended significand. | ||
// IEEE 754 double-precision numbers only require 53 bits significand. | ||
// However, in Grisu3 we need additional 11 bits so we declare m_f as UINT64. | ||
// Please note m_f does not include sign bit. | ||
UINT64 m_f; | ||
|
||
// Extended exponent. | ||
// IEEE 754 double-precision numbers only require 11 bits exponent. | ||
// However, in Grisu3 we need extra space to avoid overflow so we declare m_e as int. | ||
// Please note m_e is a biased exponent if the DiyFp instance is generated by GenerateNormalizedDiyFp(). | ||
int m_e; | ||
}; | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
// 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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.