![](/img/trans.png)
[英]If i add 2 double data type value, the result is round off and print.How do i get the exact output in double?
[英]How do I print the exact value stored in a float?
如果我將值 0.1 分配給浮點數:
float f = 0.1;
memory 中存儲的實際值不是 0.1 的精確表示,因為 0.1 不是單精度浮點格式可以精確表示的數字。 存儲的實際值——如果我的數學計算正確的話——是
0.100000001490116119384765625
但我無法找到一種方法來讓 C# 打印出該值。 即使我要求它將數字打印到很多小數位,它也沒有給出正確的答案:
// prints 0.10000000000000000000000000000000000000000000000000
Console.WriteLine(f.ToString("F50"));
如何打印存儲在浮點數中的確切值; memory 中的位模式實際代表的值?
編輯:在其他地方引起我的注意,您可以使用標准格式字符串獲得我要求的行為......在 .NET Core 和 .NET 5.0 上。 所以這個問題是 .NET 框架特定的,我猜。
這里的基本思想是先把float
轉成有理數,再把有理數轉成小數。
下面的代碼(for.Net 6,它提供了BitConverter.SingleToUInt32Bits
方法)將打印一個float
的准確值(包括NaN
值是否是quiet/signalling,NaN 的payload 以及是否設置了符號位)。 請注意, WriteRational
方法並不普遍適用於所有有理數,因為它不會嘗試檢測非終止十進制表示形式:這在這里不是問題,因為float
中的所有值都具有二次冪的分母。
using System; // not necessary with implicit usings
using System.Globalization;
using System.Numerics;
using System.Text;
static string ExactStringSingle(float value)
{
const int valueBits = sizeof(float) * 8;
const int fractionBits = 23; // excludes implicit leading 1 in normal values
const int exponentBits = valueBits - fractionBits - 1;
const uint signMask = 1U << (valueBits - 1);
const uint fractionMask = (1U << fractionBits) - 1;
var bits = BitConverter.SingleToUInt32Bits(value);
var result = new StringBuilder();
if ((bits & signMask) != 0) { result.Append('-'); }
var biasedExponent = (int)((bits & ~signMask) >> fractionBits);
var fraction = bits & fractionMask;
// Maximum possible value of the biased exponent: infinities and NaNs
const int maxExponent = (1 << exponentBits) - 1;
if (biasedExponent == maxExponent)
{
if (fraction == 0)
{
result.Append("inf");
}
else
{
// NaN type is stored in the most significant bit of the fraction
const uint nanTypeMask = 1U << (fractionBits - 1);
// NaN payload
const uint nanPayloadMask = nanTypeMask - 1;
// NaN type, valid for x86, x86-64, 68000, ARM, SPARC
var isQuiet = (fraction & nanTypeMask) != 0;
var nanPayload = fraction & nanPayloadMask;
result.Append(isQuiet
? FormattableString.Invariant($"qNaN(0x{nanPayload:x})")
: FormattableString.Invariant($"sNaN(0x{nanPayload:x})"));
}
return result.ToString();
}
// Minimum value of biased exponent above which no fractional part will exist
const int noFractionThreshold = (1 << (exponentBits - 1)) + fractionBits - 1;
if (biasedExponent == 0)
{
// zeroes and subnormal numbers
// shift for the denominator of the rational part of a subnormal number
const int denormalDenominatorShift = noFractionThreshold - 1;
WriteRational(fraction, BigInteger.One << denormalDenominatorShift, result);
return result.ToString();
}
// implicit leading one in the fraction part
const uint implicitLeadingOne = 1U << fractionBits;
var numerator = (BigInteger)(fraction | implicitLeadingOne);
if (biasedExponent >= noFractionThreshold)
{
numerator <<= biasedExponent - noFractionThreshold;
result.Append(numerator.ToString(CultureInfo.InvariantCulture));
}
else
{
var denominator = BigInteger.One << (noFractionThreshold - (int)biasedExponent);
WriteRational(numerator, denominator, result);
}
return result.ToString();
}
static void WriteRational(BigInteger numerator, BigInteger denominator, StringBuilder result)
{
// precondition: denominator contains only factors of 2 and 5
var intPart = BigInteger.DivRem(numerator, denominator, out numerator);
result.Append(intPart.ToString(CultureInfo.InvariantCulture));
if (numerator.IsZero) { return; }
result.Append('.');
do
{
numerator *= 10;
var gcd = BigInteger.GreatestCommonDivisor(numerator, denominator);
denominator /= gcd;
intPart = BigInteger.DivRem(numerator / gcd, denominator, out numerator);
result.Append(intPart.ToString(CultureInfo.InvariantCulture));
} while (!numerator.IsZero);
}
我已經根據valueBits
和fractionBits
(在方法的第一行中定義)編寫了代碼中的大部分常量,以便盡可能直接地將此方法用於double
s。 去做這個:
valueBits
更改為sizeof(double) * 8
fractionBits
更改為 52uint
s 更改為ulong
s(包括將1U
轉換為1UL
)BitConverter.DoubleToUInt64Bits
而不是BitConverter.SingleToUInt32Bits
讓這段代碼具有文化意識留給讀者作為練習:-)
是的,這是 C#(或 .net)中非常有趣的挑戰。 恕我直言,最簡單的解決方案是將 float/double 與一些巨大的數字相乘,然后將浮點結果轉換為BigInteger
。 就像,在這里我們嘗試計算1e+51*0.1
的結果:
using System.Numerics;
class HelloWorld {
static void Main() {
// Ideally, 1e+51*0.1 should be 1 followed by 50 zeros, but =>
System.Console.WriteLine(new BigInteger(1e+51*0.1));
// Outputs 100000000000000007629769841091887003294964970946560
}
}
因為浮點格式的 0.1 只是近似表示,帶有機器 epsilon 誤差。 這就是為什么我們得到這個奇怪的結果而不是 100....(50 個零)。
糟糕,這個答案與 C 相關,而不是 C#。
保留它,因為它可能提供 C# 洞察力,因為語言在這方面有相似之處。
如何打印存儲在浮點數中的確切值?
// Print exact value with a hexadecimal significant.
printf("%a\n", some_float);
// e.g. 0x1.99999ap-4 for 0.1f
要打印的價值float
與所有其他足夠鮮明的小數位十進制float
:
int digits_after_the_decimal_point = FLT_DECIMAL_DIG - 1; // e.g. 9 -1
printf("%.*e\n", digits_after_the_decimal_point, some_float);
// e.g. 1.00000001e-01 for 0.1f
用所有小數位打印十進制值是困難的 - 並且很少需要。 代碼可以使用更高的精度。 超過某個點(例如 20 個有效數字), big_value
可能會使用printf()
失去低位數字的正確性。 這種錯誤在 C 和IEEE 754 中是允許的:
int big_value = 19; // More may be a problem.
printf("%.*e\n", big_value, some_float);
// e.g. 1.0000000149011611938e-01 for 0.1f
// for FLT_TRUE_MIN and big_value = 50, not quite right
// e.g. 1.40129846432481707092372958328991613128026200000000e-45
要打印所有float
所有小數位的十進制值,請編寫一個輔助函數。 例子。
// Using custom code
// -FLT_TRUE_MIN
-0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125
對於 .NET Framework,請使用格式字符串G
。 不完全但足以應對浮動錯誤。
> (0.3d).ToString("G70")
0.29999999999999999
> (0.1d+0.2d).ToString("G70")
0.30000000000000004
您應該使用十進制而不是浮點數,如下所示:
decimal f = 0.1m;
它只會打印 0.1。
您可以檢查此答案以了解浮點數、雙精度數和小數點之間的差異
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.