I am getting a value from an OdcDataReader that comes across as a Single type. I do not want to work with a single in code and so I want to convert it into a decimal. However, it seems that anytime I try to cast it to a decimal (or anything for that matter) it loses several decimal places. I need to keep as much accuracy as possible for my calculations. The only way I could find is to convert the single to a string with a 'R' parameter (Round Trip) and then parse the string into a decimal. I created some Extension Methods to convert them but I was hoping there was a way to convert them without the string conversion and parsing. Keep in mind I need to keep the precision.
Single sng = 1.32397970f; // 1.32397974
decimal d = (decimal)sng; // 1.32398M
d = Convert.ToDecimal(sng); // 1.32398M
string xxx = sng.ToString("G"); // "1.32398"
xxx = sng.ToString("C"); // "$1.32"
xxx = sng.ToString("E"); // "1.323980E+000"
xxx = sng.ToString("e"); // "1.323980e+000"
xxx = sng.ToString("F"); // "1.32"
xxx = sng.ToString("G"); // "1.32398"
xxx = sng.ToString("N"); // "1.32"
xxx = sng.ToString("P"); // "132.40 %"
xxx = sng.ToString("R"); // "1.32397974" - Bingo!
// My extension methods
static class ExtensionMethods
{
public static decimal ToDecimal(this Single value)
{
decimal d = decimal.Parse(value.ToString("R"));
return d;
}
public static decimal ToDecimal(this Single? value, decimal dflt)
{
if (!value.HasValue)
return dflt;
decimal d = decimal.Parse(value.Value.ToString("R"));
return d;
}
public static decimal? ToDecimal(this Single? value)
{
if (!value.HasValue)
return null;
decimal d = decimal.Parse(value.Value.ToString("R"));
return d;
}
}
Convert it do a double first, then to a decimal and you will get more "precision".
decimal d = (decimal)Convert.ToDouble(sng);
You will get more precision than is actually in the single value to begin with, though.
You could do folowing:
decimal d = Convert.ToDecimal((double)sng);
that will give you result of :
1,32397973537445
I think that the following will satisfy your criteria, even though I find it strange to use a Single for a currency value. The method has successfully round tripped the value based on limited testing. The technique is based on limiting the maximum number of significant digits to 9 as that is the maximum possible for IEEE 754 single-precision binary floating-point value.
Single sng = 1.32397970f;
Decimal dec = SingleToDecimal(sng);
Single sng2 = Convert.ToSingle(dec);
bool b = sng.Equals(sng2);
private static decimal SingleToDecimal(float s)
{
// From: IEEE 754 single-precision binary floating-point format: binary32
// https://en.wikipedia.org/wiki/Single-precision_floating-point_format#IEEE_754_single-precision_binary_floating-point_format:_binary32
// The IEEE 754 standard specifies a binary32 as having:
// Sign bit: 1 bit
// Exponent width: 8 bits
// Significand precision: 24 bits (23 explicitly stored)
//This gives from 6 to 9 significant decimal digits precision
const int maxSignificantDigits = 9;
decimal sign = (s < 0F) ? decimal.MinusOne : decimal.One;
s = Math.Abs(s);
double whole = Convert.ToDouble(Math.Floor(s));
double fraction = s - whole;
int roundOffDigits = 0;
if (whole > 0)
{
int numDigitsInWhole = Convert.ToInt32(Math.Floor(Math.Log10(whole))) + 1;
roundOffDigits = maxSignificantDigits - numDigitsInWhole;
}
else
{
int numLeadZerosInFraction = Convert.ToInt32(Math.Floor(-Math.Log10(fraction)));
roundOffDigits = maxSignificantDigits + numLeadZerosInFraction;
}
fraction = Math.Round(fraction, roundOffDigits, MidpointRounding.ToEven);
return new decimal(fraction + whole) * sign;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.