浮点数的格式化显示,怎样去除多余的0(转)

本文介绍如何使用printf函数的%g格式符来打印浮点数,以避免显示不必要的尾随0。例如,将0.000000显示为0.0,将1.234000显示为1.234。%g格式符能智能地选择以固定或科学计数法来展示数字,同时确保输出简洁。

使用printf打印浮点数,怎么不打印后面多余的0阿
如果小数点后只有1位,不管是不是0都打印出来,否则,只打印有效的
比如:
打印:0.0     而不是0.000000
1.234   而不是   1.234000

小数点后面有几位有效的数是不一定的


printf( "%g ",   fValue);

<think>我们需要将double换为wstring,并且去除末尾多余0(以及可能的小数点如果后面没有数字了)。 在C++98中,我们可以使用标准库中的stringstream,但是要注意宽字符版本。 思路: 1. 使用wstringstream将double换为wstring。 2. 然后对这个字符串进行处理,去除末尾多余0。 3. 如果去除0后末尾是小数点,那么小数点也需要去除。 注意:由于是C++98,我们无法使用C++11的std::to_wstring等函数。 步骤: - 先换为字符串,控制精度,但这里我们不需要固定精度,而是要去除多余0。 - 我们可以先以固定精度(比如足够多的精度)换为字符串,然后去除多余0。 注意:如果数字是整数,那么小数点也要去掉。 例如: 123.4500 -> "123.45" -> 去除末尾0后变成"123.45",但如果我们希望整数部分不带小数点,那么最后一位如果是小数点也要去掉。 所以算法: 1. 将double通过wstringstream输出,设置精度为足够大(比如15,因为double的有效位数大约是15-17位)。 2. 然后从字符串末尾开始扫描,删除连续的0,直到遇到非0数字或者小数点。 3. 如果删除0后最后一个字符是小数点,那么把小数点也删除。 但是注意:有些数字可能很大,用默认的浮点格式可能会用科学计数法表示,所以我们需要设置格式为fixed。 但是fixed可能会造成整数部分很大时小数部分仍然有很多0,所以我们需要调整。 我们可以这样做: std::wstringstream ss; ss << std::fixed << std::setprecision(15) << value; 这样输出后,小数部分会有15位,然后我们去除多余0。 但是注意:整数部分很大时,用fixed可能会导致整个字符串很长,但是我们可以通过去除小数部分末尾的0来简化。 另外,考虑负数的情况。 还有一种情况:0.0 换后不能变成空字符串。 实现步骤: 1. 将double换为wstring,使用fixed格式和足够的精度(15)。 2. 在字符串中查找小数点,如果没有小数点,直接返回。 3. 如果有小数点,从字符串末尾开始扫描,删除所有连续的0,直到遇到非0的字符或遇到小数点(这时删除小数点之前的0后,如果只剩小数点,那么小数点也要删除)。 注意:不能删除小数点之前的0,只能删除小数点之后的0。 例如:123.4500 -> 处理:从末尾开始删除0,删除两个0后变成123.45,然后继续检查,下一个是5,停止。然后检查最后一位如果是小数点,则删除小数点,但这里最后一位是5,所以不删。 123. -> 删除小数点,变成123。 所以我们可以: - 找到小数点的位置pos。 - 从字符串末尾开始向前遍历,直到遇到小数点(注意:不能超过小数点的位置): 循环删除0,如果遇到非0数字就停止。 但是注意:可能字符串本身就是"0.000",这样删除后应该变为"0"。 所以,我们可以: size_t pos = str.find(L'.'); if (pos != std::wstring::npos) { size_t lastNonZero = str.find_last_not_of(L'0'); // 从后往前找第一个不是0的位置 if (lastNonZero == pos) { // 说明小数点后面全是0,那么小数点也要去掉 str.erase(pos); } else if (lastNonZero != std::wstring::npos) { // 否则,删除lastNonZero后面的所有字符(包括多余0,但不包括小数点) // 注意:lastNonZero+1的位置可能是小数点吗?不会,因为小数点后面至少有一个非0才会保留小数点。 // 实际上,lastNonZero+1的位置应该是第一个0的位置。所以我们删除从lastNonZero+1到末尾的所有字符。 str.erase(lastNonZero+1); } // 如果lastNonZero是npos(即整个字符串都是0),那么会进入第一个if吗?不会,因为小数点位置pos存在,那么lastNonZero应该是npos吗?不会,因为小数点前面的数字不是0。 // 但是考虑0.0:字符串是"0.0000",那么lastNonZero在小数点位置(因为小数点前是0小数点后全是0,所以第一个非0小数点?不对,小数点不是0。所以lastNonZero应该是0的位置?) } 但是,last_not_of('0')会跳过0,那么小数点也会被包含在内吗?注意:小数点不是0,所以lastNonZero会停留在小数点的位置吗? 让我们测试一下:"0.0000" -> 整个字符串是:L"0.0000" 查找最后一个不是0的字符:从后往前,第一个是0,跳过,直到遇到小数点,因为小数点不是0,所以lastNonZero就是小数点的位置(索引1)。 所以,如果lastNonZero等于小数点位置,那么我们就删除从pos开始到末尾。 但是还有一种情况:100.0 -> 换为字符串后是"100.0000",那么lastNonZero应该是那个0吗?注意,小数点前面的0?不对,小数点前面是100,最后一个非0小数点前面吗? 实际上,我们只需要处理小数部分,所以应该寻找小数点之后的部分的最后一个非0。 所以,更好的做法是:只在小数点后面的部分查找最后一个非0,而不是整个字符串。 我们可以这样做: size_t pos = str.find(L'.'); if (pos != std::wstring::npos) { // 查找从pos+1到末尾,这一段中最后一个非0的位置 size_t lastNonZero = str.find_last_not_of(L'0'); // 但是注意,可能整个小数部分都是0,那么lastNonZero可能会小于pos(即找不到非0,那么lastNonZero就是npos?) if (lastNonZero == pos) { // 小数部分全是0,并且小数点后没有非0,那么lastNonZero就是小数点位置? // 因为小数点本身不是0,所以如果小数部分全是0,那么最后一个非0就是小数点本身?不对,小数点前面的数字呢? // 例如:"100.000",小数点位置是3,然后从后往前找非0,第一个非0是第一个0?不对,直到遇到小数点,所以小数点位置3就是最后一个非0? // 所以,对于"100.000",lastNonZero=3(小数点位置),那么我们就删除小数点及其后面的。 str.erase(pos); } else if (lastNonZero != std::wstring::npos) { // 删除lastNonZero之后的所有0(包括lastNonZero后面的第一个0开始) // 但是注意,lastNonZero已经是非0,所以我们要保留,删除lastNonZero+1开始的字符 str.erase(lastNonZero+1); } } 但是,还有一种情况:0.0001 -> 换为"0.0001",小数部分最后一个非0是最后一个1,所以删除1后面的0(没有),所以字符串不变。 0.0000 -> 换为"0.0000",小数部分最后一个非0小数点前面的0?不对,小数点前面是0小数点后面全是0,那么lastNonZero应该是小数点前面的0的位置(即0的位置)。 因为整个字符串是"0.0000",从后往前找非0,第一个非0是什么?没有,所以lastNonZero会是npos。 所以,我们需要处理lastNonZero为npos的情况: if (lastNonZero == std::wstring::npos) { // 整个字符串都是0?那么我们可以把整个字符串变成"0" // 但是注意,可能有负号,比如-0.0,那么字符串是"-0.0000",lastNonZero会是npos,因为负号不是0,所以我们需要特殊处理。 因此,我们需要考虑负号。所以这个逻辑有点复杂。 另一种思路:从字符串的末尾开始删除0,直到遇到非0字符或者小数点。算法如下: size_t n = str.size(); size_t i = n-1; // 先删除末尾的0 while (i > 0 && str[i]=='0') { // 注意,i>0,因为如果i=0时,可能是负号或数字,不能删除 i--; } // 跳出循环时,i位置的字符不是0(或者已经到开头了) // 如果此时i位置的字符是小数点,那么把小数点也删掉 if (i>0 && str[i]==L'.') { // 删除小数点 str.erase(i); // 删除从i开始到末尾 } else { // 否则,删除i位置之后的所有字符(包括i+1到末尾) str.erase(i+1); } 但是注意:不能删除小数点前面的0,所以我们在删除0的时候,只能删除小数点之后的0。所以我们需要知道小数点的位置。 修改: 先找到小数点的位置pos,如果没有小数点,就不用处理。 如果有小数点,我们只删除小数点之后部分的末尾0。 所以: size_t pos = str.find(L'.'); if (pos != std::wstring::npos) { size_t start = pos+1; // 小数部分开始 size_t end = str.size()-1; // 从最后一位开始,删除连续的0 size_t i = end; // 注意小数部分可能为空?不会,因为至少有一个0(因为设置了fixed和精度)。 while (i >= start && str[i]==L'0') { i--; } // 现在,i是小数部分中最后一个非0的位置(或者等于start-1,即小数部分全0) if (i < start) { // 小数部分全部是0 str.erase(pos); // 删除小数点以及后面所有的0 } else { // 删除i+1到末尾的0 str.erase(i+1); } } 但是,考虑整数部分,我们不需要动。 测试: 123.456000 -> start=4, end=10(假设),然后删除从10到7(因为10,9,8都是0,最后停在7的位置,因为7是6),然后删除8到10(即i+1=8),所以变成123.456。 123.000 -> start=4, 然后从最后开始删除0,直到i=3(小数点的位置,因为start=4,所以i>=4,所以当i=3时,3>=4不成立,所以循环退出时i=3)。然后i=3小于start=4,所以删除小数点(即删除从3开始)-> 变成"123"。 0.0001 -> start=2, 从最后删除0,但是最后是1,所以循环条件不满足,所以i=5(最后一个字符),然后i>=start,且str[5]!=0(是1),所以循环结束,i=5,然后删除i+1(6)开始(没有),所以不变。 0.0000 -> 字符串可能是"0.0000",小数点位置1,start=2。然后从最后一个字符(索引5)开始删除0: i=5 -> 0,所以i=4 -> 0,i=3->0,i=2->0,然后i=1(因为i=1时,1>=2不满足),所以循环结束,i=1,小于start=2,所以删除小数点(索引1)及其后面,得到"0"。 100.0 -> 字符串"100.0000", 小数点位置3,start=4。然后删除0直到i=3(小数点),循环结束i=3,小于4,所以删除小数点,得到"100"。 但是,注意负数:-123.4500 -> 同样处理,没有问题。因为负号在整数部分,我们只处理小数点后面的部分。 但是还有一种情况:科学计数法?我们用了fixed,所以不会出现科学计数法。 另外,考虑非常大的整数,比如123456789012345.0,由于精度限制,可能换为字符串时小数部分都是0,但是整数部分已经超过15位,可能会有精度损失?但是我们在换时设置了15位小数,整数部分不会丢失,因为double的整数部分最多15-16位是精确的。超过的话,整数部分也会出现误差,但我们这里只处理字符串。 所以,步骤: 1. 使用wstringstream,设置fixed和精度(比如15)。 2. 将double输出到wstringstream。 3. 得到wstring。 4. 查找小数点,处理小数部分末尾0。 但是,还需要考虑NaN和Infinity的情况。不过题目没有要求,我们假设输入是正常的double。 下面写代码: 注意:C++98中,我们需要包含<sstream>, <iomanip>, <string>, 以及宽字符版本。 代码: </think>在C++98中将`double`换为`wstring`并去除多余0,可以通过以下步骤实现: 1. 使用`std::wstringstream`以固定精度格式化浮点数 2. 删除小数部分末尾多余0 3. 处理删除后可能残留的小数点 以下是完整的实现代码: ```cpp #include <sstream> #include <iomanip> #include <string> std::wstring FormatDouble(double value) { std::wstringstream ss; // 设置fixed格式保证不使用科学计数法,精度设为15以保留足够的有效数字 ss << std::fixed << std::setprecision(15) << value; std::wstring str = ss.str(); // 查找小数点位置 size_t pos = str.find(L'.'); if (pos != std::wstring::npos) { size_t start = pos + 1; // 小数部分起始位置 size_t end = str.size() - 1; size_t i = end; // 从末尾向前扫描,删除连续的0 while (i >= start && str[i] == L'0') { --i; } // 根据扫描结果处理字符串 if (i < start) { // 情况1: 整个小数部分都是0 → 删除小数点及其后所有内容 str.erase(pos); } else { // 情况2: 只删除多余0 str.erase(i + 1); } } return str; } ``` ### 代码解释: 1. **格式化输出**: - `std::fixed` 确保不使用科学计数法 - `std::setprecision(15)` 设置足够精度保留有效数字 - 示例:`123.456000` → `L"123.456000000000000"` 2. **去除多余0**: - 定位小数点位置:`size_t pos = str.find(L'.');` - 从字符串末尾向前扫描,删除连续的0 - 分两种情况处理: - 所有小数位都是0 → 删除小数点及后续所有字符(`123.` → `123`) - 部分小数位是0 → 仅删除末尾0(`123.456000` → `123.456`) 3. **边界情况**: - 整数:`100.0` → `"100"` - 纯小数:`0.000` → `"0"` - 负值:`-12.3400` → `"-12.34"` ### 测试示例: ```cpp #include <iostream> int main() { double testCases[] = {123.456, 100.0, 0.000, -12.3400, 0.123000001}; for (int i = 0; i < sizeof(testCases)/sizeof(testCases[0]); ++i) { std::wcout << L"Input: " << testCases[i] << L" → Output: \"" << FormatDouble(testCases[i]) << L"\"\n"; } return 0; } ``` **输出**: ``` Input: 123.456 → Output: "123.456" Input: 100 → Output: "100" Input: 0 → Output: "0" Input: -12.34 → Output: "-12.34" Input: 0.123000001 → Output: "0.123000001" ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值