C++ 如何“计算”双精度值的正确小数位数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4217510/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to 'cout' the correct number of decimal places of a double value?
提问by Chan
I need help on keeping the precision of a double
. If I assign a literal to a double, the actual value was truncated.
我需要帮助来保持 a 的精度double
。如果我将文字分配给双精度值,则实际值会被截断。
int main() {
double x = 7.40200133400;
std::cout << x << "\n";
}
For the above code snippet, the output was 7.402
Is there a way to prevent this type of truncation? Or is there a way to calculate exactly how many floating points for a double
? For example, number_of_decimal(x)
would give 11, since the input is unknown at run-time so I can't use setprecision()
.
对于上面的代码片段,输出7.402
是有没有办法防止这种类型的截断?或者有没有办法准确计算 a 的浮点数double
?例如,number_of_decimal(x)
将给出 11,因为输入在运行时未知,所以我不能使用setprecision()
.
I think I should change my question to: How to convert a double to a string without truncating the floating points. i.e.
我想我应该将我的问题更改为:如何在不截断浮点数的情况下将双精度数转换为字符串。IE
#include <iostream>
#include <string>
#include <sstream>
template<typename T>
std::string type_to_string( T data ) {
std::ostringstream o;
o << data;
return o.str();
}
int main() {
double x = 7.40200;
std::cout << type_to_string( x ) << "\n";
}
The expected output should be 7.40200 but the actual result was 7.402. So how can I work around this problem? Any idea?
预期输出应为 7.40200,但实际结果为 7.402。那么我该如何解决这个问题呢?任何的想法?
回答by fredoverflow
Due to the fact the float
and double
are internally stored in binary, the literal 7.40200133400
actually stands for the number 7.40200133400000037653398976544849574565887451171875
由于float
和double
内部存储为二进制的事实,文字7.40200133400
实际上代表数字 7.40200133400000037653398976544849574565887451171875
...so how much precision do you really want? :-)
...那么你真正想要多少精度?:-)
#include <iomanip>
int main()
{
double x = 7.40200133400;
std::cout << std::setprecision(51) << x << "\n";
}
And yes, this program really prints 7.40200133400000037653398976544849574565887451171875!
是的,这个程序真的打印了 7.40200133400000037653398976544849574565887451171875!
回答by Maxpm
You must use setiosflags(ios::fixed)
and setprecision(x)
.
您必须使用setiosflags(ios::fixed)
和setprecision(x)
。
For example, cout << setiosflags(ios::fixed) << setprecision(4) << myNumber << endl;
例如, cout << setiosflags(ios::fixed) << setprecision(4) << myNumber << endl;
Also, don't forget to #include <iomanip.h>
.
另外,不要忘记#include <iomanip.h>
。
回答by Kos
std::cout << std::setprecision(8) << x;
Note that setprecision
is persistent and all next floats you print will be printed with that precision, until you change it to a different value. If that's a problem and you want to work around that, you can use a proxy stringstream
object:
请注意,这setprecision
是持久的,并且您打印的所有下一个浮点数都将以该精度打印,直到您将其更改为不同的值。如果这是一个问题并且您想解决这个问题,您可以使用代理stringstream
对象:
std::stringstream s;
s << std::setprecision(8) << x;
std::cout << s.str();
For more info on iostream formatting, check out the Input/output manipulatorssection in cppreference.
有关 iostream 格式的更多信息,请查看cppreference 中的输入/输出操纵器部分。
回答by Daniel Lidstr?m
Solution using Boost.Format:
使用Boost.Format 的解决方案:
#include <boost/format.hpp>
#include <iostream>
int main() {
double x = 7.40200133400;
std::cout << boost::format("%1$.16f") % x << "\n";
}
This outputs 7.4020013340000004
.
这输出7.4020013340000004
.
Hope this helps!
希望这可以帮助!
回答by Nim
The only answer to this that I've come up with is that there is no way to do this (as in calculate the decimal places) correctly! THE primary reason for this being that the representation of a number may not be what you expect, for example, 128.82, seems innocuous enough, however it's actual representation is 128.8199999999... how do you calculate the number of decimal places there??
我想出的唯一答案是没有办法正确地做到这一点(如计算小数位)!造成这种情况的主要原因是数字的表示可能不是您所期望的,例如 128.82,似乎无害,但它的实际表示是 128.8199999999 ......你如何计算那里的小数位数?
回答by Mark B
Responding to your answer-edit: There is no way to do that. As soon as you assign a value to a double
, any trailing zeroes are lost (to the compiler/computer, 0.402, 0.4020, and 0.40200 are the SAME NUMBER). The only way to retain trailing zeroes as you indicated is to store the values as strings (or do trickery where you keep track of the number of digits you care about and format it to exactly that length).
回应您的答案编辑:没有办法做到这一点。一旦为 a 赋值double
,所有尾随零都将丢失(对于编译器/计算机,0.402、0.4020 和 0.40200 是相同的数字)。按照您的指示保留尾随零的唯一方法是将值存储为字符串(或者做一些技巧,跟踪您关心的位数并将其格式化为该长度)。
回答by user207421
Doubles don't havedecimal places. They have binary places. And binary places and decimal places are incommensurable (because log2(10)
isn't an integer).
双打不具有小数。他们有二进制的地方。并且二进制位和小数位是不可通约的(因为log2(10)
不是整数)。
What you are asking for doesn't exist.
你要的东西不存在。
回答by fned
Let s make an analogous request: after initialising an integer with 001, you would want to print it with the leading zeroes. That formatting info was simply never stored.
让我们做一个类似的请求:在用 001 初始化一个整数后,你想用前导零打印它。格式信息根本没有被存储。
For further understanding the double precision floating point storage, look at the IEEE 754 standard.
要进一步了解双精度浮点存储,请查看 IEEE 754 标准。
回答by Cheers and hth. - Alf
The second part of the question, about how to preserve trailing zeroes in a floating point value from value specification to output result, has no solution. A floating point value doesn't retain the original value specification. It seems this nonsensical part was added by an SO moderator.
问题的第二部分,关于如何从值规范到输出结果保留浮点值中的尾随零,没有解决方案。浮点值不保留原始值规范。似乎这个荒谬的部分是由 SO 版主添加的。
Regarding the first and original part of the question, which I interpret as how to present all significant digits of 7.40200133400
, i.e. with output like 7.402001334
, you can just remove trailing zeroes from an output result that includes only trustworthy digits in the double
value:
关于问题的第一部分和原始部分,我将其解释为如何呈现 的所有有效数字7.40200133400
,即输出类似7.402001334
,您可以从仅包含double
值中可信数字的输出结果中删除尾随零:
#include <assert.h> // assert
#include <limits> // std::(numeric_limits)
#include <string> // std::(string)
#include <sstream> // std::(ostringstream)
namespace my{
// Visual C++2017 doesn't support comma-separated list for `using`:
using std::fixed; using std::numeric_limits; using std::string;
using std::ostringstream;
auto max_fractional_digits_for_positive( double value )
-> int
{
int result = numeric_limits<double>::digits10 - 1;
while( value < 1 ) { ++result; value *= 10; }
return result;
}
auto string_from_positive( double const value )
-> string
{
ostringstream stream;
stream << fixed;
stream.precision( max_fractional_digits_for_positive( value ) );
stream << value;
string result = stream.str();
while( result.back() == '0' )
{
result.resize( result.size() - 1 );
}
return result;
}
auto string_from( double const value )
-> string
{
return (0?""
: value == 0? "0"
: value < 0? "-" + string_from_positive( -value )
: string_from_positive( value )
);
}
}
#include<iostream>
auto main()
-> int
{
using std::cout;
cout << my::string_from( 7.40200133400 ) << "\n";
cout << my::string_from( 0.00000000000740200133400 ) << "\n";
cout << my::string_from( 128.82 ) << "\n";
}
Output:
输出:
7.402001334 0.000000000007402001334 128.81999999999999
You might consider adding logic for rounding to avoid long sequences of 9's, like in the last result.
您可能会考虑添加四舍五入逻辑以避免出现长 9 序列,就像最后一个结果一样。