如何将固定精度的两倍格式化为给定的长度

How to format a double of fixed precision to a given length?

本文关键字:格式化 两倍 精度      更新时间:2023-10-16

我正在使用格式的输出,例如

std::cout << " " << std::setw(7) << std::setprecision(5) << Value ;

准备桌子的目标。但是,表似乎是这样:

     0  6.0303  16.052  40.523     100  40.557  16.167  6.7314  1.8967       0
     0  4.5593   13.16  25.342  41.927  25.354  13.312  4.9988  1.9527       0
     0  3.0952  6.6864  13.531   17.01  13.544  6.7291   3.466 0.91553       0
     0  1.1353   3.466  5.0842  7.3242  5.0842  3.4981  1.2207 0.56076       0
     0 0.54474 0.95825   2.153  2.1179   2.153 0.95825  0.5928 0.10681       0
     0 0.085449 0.38452 0.45166 0.78392 0.45166 0.38452 0.085449 0.048065       0
     0 0.032043 0.042725 0.15221 0.11444 0.15221 0.042725 0.032043       0       0
     0       0       0       0       0       0       0       0       0       0

看起来小数点之后的第一个数字为零,则显示长度更长。我如何避免它?

使用:

std::cout << std::fixed;

默认行为是在"精度"中显示数字数。STD ::固定是显示精确的数字数量。因此,默认情况下您将显示5个重要数字。在固定的5个数字上。
例如:

auto a = 2017.0;
default = 2017
fixed = 2017.00000
auto b = 1e-10;
default= 1e-10
fixed = 0.00000

hm。因此,我想在考虑所有边缘案例后要这样做。我敢肯定,我不处理一些正确的事,我认为这在C中会更干净,但是我认为这还可以(如果经过工程的方式(。

如果您知道发生了什么,请随时用评论进行编辑:(。

除了一些警告:

这已经超过了工程,并且经过测试。

不是便携式的,但可以通过更多的工作来制作。

我在您的输入中添加了额外的测试/角案。

这可能比您需要的要多,但是您可以将其推广。或使用更好的库。

#include <cmath>                                                                                
#include <cstdint>                                                                              
#include <iomanip>                                                                              
#include <iostream>                                                                             
#include <vector>                                                                               
const std::vector<std::vector<double>> array = {                                                
  // OP's data.                                                                                 
    {0, 6.0303, 16.052, 40.523, 100, 40.557, 16.167, 6.7314, 1.8967, 0},                        
    {0, 4.5593, 13.16, 25.342, 41.927, 25.354, 13.312, 4.9988, 1.9527, 0},                      
    {0, 3.0952, 6.6864, 13.531, 17.01, 13.544, 6.7291, 3.466, 0.91553, 0},                      
    {0, 1.1353, 3.466, 5.0842, 7.3242, 5.0842, 3.4981, 1.2207, 0.56076, 0},                     
    {0, 0.54474, 0.95825, 2.153, 2.1179, 2.153, 0.95825, 0.5928, 0.10681, 0},                   
    {0, 0.085449, 0.38452, 0.45166, 0.78392, 0.45166, 0.38452, 0.085449, 0.048065, 0},          
    {0, 0.032043, 0.042725, 0.15221, 0.11444, 0.15221, 0.042725, 0.032043, 0, 0},               
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},                                                             
  // My test data.                                                                              
    {1e-7, 1.12031e-34, 1e-34, 1e-6, 1e33, 1e-8, 1.3123233e93, 1e32, 1.e99, 1.e100},            
    {-1e-7, -1.12031e-34, -1e-34, -1e-6, -1e33, -1e-8, -1.3123233e93, -1e32, -1.e99, -1.09e100},
};                                                                                              
// Warning: any widths under 7 cannot accurately print the double -1e+100...                    
// (or many like it). There's just no way to express that any more tersely!                     
constexpr uint8_t kWidth = 7;                                                                   
// Most of these are really obvious but help make the code slightly cleaner.                    
// "."                                                                                          
constexpr uint8_t kWidthTakenByDecimalSeparator = 1;                                            
// "-"                                                                                          
constexpr uint8_t kWidthTakenByNegativeSign = 1;                                                
// "e+" or "e-"                                                                                 
constexpr uint8_t kWidthOfExtraScientificNotationCharacters = 2;                                
// On *MY* system, 1.0e-08 is always printed, not 1.0e-8. This basically handles                
// the leading 0 on the exponent. Tune accordingly (or find the right compiler                  
// flags that give you the right number(s).                                                     
constexpr uint8_t kMinimumWidthOfScientificNotationExponent = 2;                                
int main() {                                                                                    
  // Store the original std::cout flags.                                                        
  auto old_flags = std::cout.flags();                                                           
  for (auto& row : array) {                                                                     
    for (auto& value : row) {                                                                   
      const double log10_value = log10(std::abs(value));                                        
      const uint8_t available_width = kWidth - (value < 0) * kWidthTakenByNegativeSign;         
      // Handle numbers greater than 10^kWidth or less than 10^-kWidth in                       
      // scientific notation.                                                                   
      const bool use_scientific_notation =                                                      
          value != 0 &&                                                                         
          (log10_value < -1.0 * available_width + kWidthTakenByDecimalSeparator +               
                             kWidthTakenByNegativeSign ||                                       
           log10_value > available_width);                                                      
      std::cout << ' ' << std::setw(kWidth);                                                    
      if (use_scientific_notation) {                                                            
        const double log10log10_value = log10(log10_value);                                     
        const uint8_t num_digits_in_exponent_of_scientific_notation =                           
            std::max(kMinimumWidthOfScientificNotationExponent,                                 
                     static_cast<uint8_t>(log10log10_value ));                                  
        const uint8_t num_digits_desired_after_decimal_scientific =                             
            std::max(1.0, log10log10_value);                                                    
        uint8_t potential_new_precision =                                                       
            available_width - num_digits_in_exponent_of_scientific_notation -                   
            kMinimumWidthOfScientificNotationExponent -                                         
            kWidthTakenByDecimalSeparator -                                                     
            num_digits_desired_after_decimal_scientific;                                        
        // Lazy underflow checking. Needed?                                                     
        if (potential_new_precision > available_width) {                                        
          potential_new_precision = 0;                                                          
        }                                                                       
        std::cout << std::scientific                                            
                  << std::setprecision(potential_new_precision);                
      } else {                                                                  
        // We take the max with 1.0 to compensate for the leading 0 in decimals.
        // If your system doesn't put a leading zero there, remove it.          
        const uint8_t number_of_digits_before_decimal_fixed =                   
            std::max(log10_value + 1.0, 1.0);                                   
        const uint8_t number_of_digits_desired_after_decimal_fixed =            
            available_width - kWidthTakenByDecimalSeparator -                   
            number_of_digits_before_decimal_fixed;                              
        // Fixed width decimals -- this means that the code                     
        //   std::cout << std::precision(n)                                     
        // tells the stream to use n digits *after the decimal*.                
        std::cout << std::fixed                                                 
                  << std::setprecision(                                         
                         number_of_digits_desired_after_decimal_fixed);         
      }                                                                         
      std::cout << value;                                                       
    }                                                                           
    std::cout << 'n';                                                          
  }                                                                             
  // Reset the flags to their old value, and flush the output buffer.           
  std::cout.flush();                                                            
  std::cout.flags(old_flags);                                                   
}       

和有趣的东西:

用kwidth = 7:

输出示例
0.00000 6.03030 16.0520 40.5230 100.000 40.5570 16.1670 6.73140 1.89670 0.00000
0.00000 4.55930 13.1600 25.3420 41.9270 25.3540 13.3120 4.99880 1.95270 0.00000
0.00000 3.09520 6.68640 13.5310 17.0100 13.5440 6.72910 3.46600 0.91553 0.00000
0.00000 1.13530 3.46600 5.08420 7.32420 5.08420 3.49810 1.22070 0.56076 0.00000
0.00000 0.54474 0.95825 2.15300 2.11790 2.15300 0.95825 0.59280 0.10681 0.00000
0.00000 0.08545 0.38452 0.45166 0.78392 0.45166 0.38452 0.08545 0.04806 0.00000
0.00000 0.03204 0.04272 0.15221 0.11444 0.15221 0.04272 0.03204 0.00000 0.00000
0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000
1.0e-07 1.1e-34 1.0e-34 1.0e-06 1.0e+33 1.0e-08 1.3e+93 1.0e+32 1.0e+99  1e+100
 -1e-07  -1e-34  -1e-34  -1e-06  -1e+33  -1e-08  -1e+93  -1e+32  -1e+99 -1e+100

示例输出kwidth = 10:

0.00000000 6.03030000 16.0520000 40.5230000 100.000000 40.5570000 16.1670000 6.73140000 1.89670000 0.00000000
0.00000000 4.55930000 13.1600000 25.3420000 41.9270000 25.3540000 13.3120000 4.99880000 1.95270000 0.00000000
0.00000000 3.09520000 6.68640000 13.5310000 17.0100000 13.5440000 6.72910000 3.46600000 0.91553000 0.00000000
0.00000000 1.13530000 3.46600000 5.08420000 7.32420000 5.08420000 3.49810000 1.22070000 0.56076000 0.00000000
0.00000000 0.54474000 0.95825000 2.15300000 2.11790000 2.15300000 0.95825000 0.59280000 0.10681000 0.00000000
0.00000000 0.08544900 0.38452000 0.45166000 0.78392000 0.45166000 0.38452000 0.08544900 0.04806500 0.00000000
0.00000000 0.03204300 0.04272500 0.15221000 0.11444000 0.15221000 0.04272500 0.03204300 0.00000000 0.00000000
0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000
0.00000010 1.1203e-34 1.0000e-34 0.00000100 1.0000e+33 0.00000001 1.3123e+93 1.0000e+32 1.0000e+99 1.000e+100
-0.0000001 -1.120e-34 -1.000e-34 -0.0000010 -1.000e+33 -1.000e-08 -1.312e+93 -1.000e+32 -1.000e+99 -1.09e+100

那比我想象的要多。